Java基础
javap.exe是Java类的分解器,即对.class文件提供字节代码的反汇编,并打印。
java.exe,Java解释器、执行字节码程序;
javac.exe,Java的编译器,能将源文件编译成字节码文件;
jdb.exe,Java调试器.
除了IDE工具,还有什么可以编译Java文件
javac、maven、ANT
入口
main方法
命名规范
所有命名均遵循见名知意
不允许拼音和英文混合使用
变量
- 驼峰命名
- 首字母小写
项目名
- 全小写
包名
- 全小写
- 域名倒置
常量
- 全大写
- 用下划线连接
类
- 首字母大写
- 驼峰命名
类和对象
类是对象的抽象体
类的定义
类的默认修饰符是default
类只能用public和不写修饰符用默认default修饰
-
一般用public修饰
-
一个类只能有一个公共类
每个类只能有一个对外开放的接口
如果不用public修饰,该类不可被使用
该类将会变的没有意义 -
基本的类包含属性和方法
对象的创建
- new关键字
- 创建对象时根据构造器对对象成员变量进行初始化
对象的引用
- 对象赋给引用变量
- 引用变量存对象的地址
this
this总是指向调用该方法的对象
-
构造器中this指向正在初始化的对象
-
方法中,this指向调用该方法的对象
-
在静态方法中不可使用this
静态方法属于类本身,不属于对象,如果在静态方法中使用this,则jvm不知道this指向谁
-
必须写在构造器第一行
类中元素
-
成员变量
-
方法
-
初始化块
-
构造方法
-
内部类
- 接口
- 枚举
内部类
特征
- 内部类更好的封装隐藏,不允许同包下其他类访问
- 内部类可以直接访问外部类私有数据,反之不行
- 匿名内部类适用于创建仅需使用一次的类
分类
-
成员内部类
-
将一个类定义在另一个类的类中
public class C{
class D{}
}
-
成员内部类可以无条件访问外部类数据,外部类需要创建内部类对象,通过对象访问
-
若内部类与外部类发生重名,则这些数据在内部类将被隐藏
访问在内部类被隐藏的外部类数据的方式:
外部类.this.属性/方法。 -
不可包含静态属性和方法
-
-
局部内部类
-
局部内部类存在于方法中。和成员内部类的区别:局部内部类的访问权限仅限于方法或作用域内
class K{
public void say(){
class J{} }
}
-
局部内部类只能访问外部类的final变量
-
不能由权限修饰符和static修饰
-
-
匿名内部类
public class Test13 {
public static void main(String[] args) {
driveCar(new Car(){
@Override
public void drive() {
System.out.println(‘驾驶着BMW汽车’);
}
});
}
public static void driveCar(Car car){
car.drive();
}
}interface Car {
void drive();
}- 没有构造方法
- 只能访问外部类的final变量
- 传参如果是一个接口,可以直接在方法括号内创建接口的匿名内部实现类
-
静态内部类
- 由static修饰的内部类
- 不可访问外部类的非静态变量和方法
- 静态内部类不可包含非静态成员
不同点
-
权限修饰符
- private
- protected
- static
- 默认
- public
-
非静态内部类不能拥有静态成员
-
非静态内部类不可以拥有静态初始化块,可以拥有普通初始化块
内部类实例的创建
- 外部类.内部类 对象名 = new 外部类.内部类();
- 外部类.内部类 对象名 = 外部类对象名.new 内部类();
面向对象三大特征
封装
将类如何实现隐藏起来,只提供公共方法,让外类通过公共方法修改与查询数据
-
本质
- 隐藏具体细节,实现模块化编程
-
类和类之间的关系
-
泛化关系
- 继承
- 实现
-
依赖关系
- 例如人类拧螺丝方法,需要依赖工具类的实例
-
关联关系
- 若A与B有关联,说明AB两个类有关系,他们两个并不能共同构成一个整体,
例如人类和车有关联,但人类和车类并不能组合起来构成一个更大的整体
但妻子丈夫儿女有关联且能构成一个家庭,这叫聚合
- 若A与B有关联,说明AB两个类有关系,他们两个并不能共同构成一个整体,
-
聚合关系
-
聚合关系是建立在关联关系之上的,两个类是聚合关系那么一定相关联,反之则否
-
弱聚合
-
通常把弱聚合称为聚合
-
若AB弱聚合关系,则AB可以共同构成更大的整体,但AB不用同时出现,例如丈夫妻子和儿子,当儿子不出现的时候,丈夫和妻子
也为一个家庭,而后可以出现不同个儿子共同构成家庭,弱聚合就是一个类中引用了另一个类,但并不需要在实例化该类时就实例化这个引用
-
-
强聚合
- 通常把强聚合称为组合
- 若两个类具有强聚合关系,则一个类在实例化时就必须把另一个类实例化,例如选择题类和选项类,在一道选择题
被实例化的同时需要实例化n个选项,即两个类必须同时出现
-
-
继承
-
extends关键字
-
子类获得父类全部成全变量和方法,但仅部分可被公开访问
-
子类可获得父类静态方法
但实际意义上是错误的,类方法只属于类,不要把静态与继承混为一谈
-
子类可获得父类静态属性
静态属性只属于该类,虽然子类也可以通过类名去调用父类静态属性,但这中做法是错误的,不能因为可以调用就说子类继承到了父类的静态属性或者方法。
-
-
java中只可单继承
-
继承抽象类必须重写抽象类中的所有抽象方法
-
重写
-
重写父类方法将覆盖父类方法
-
子类对象不可调用被覆盖方法
-
子类中可以调用被覆盖方法
- 实例方法用关键字super调用
- 类方法用类名调用
-
@Override
- 注解
- 只能标注方法
-
重写规则
-
重写权限修饰符不能变小
-
重写必须方法名一致参数列表一致,返回值类型可不同
父类被重写方法的返回值可以是子类重写方法的返回值的父类,反之不可以
-
不能重写私有方法
-
用private修饰的方法对子类隐藏,无法构成重写
-
-
-
super
super调用父类构造器用法与this调用本类重载构造器很相似,并且都必须写在构造器的第一行,所以this与super不可同时出现
-
用于限定该对象调用他从父类继承到的东西
包括因重写而被覆盖的父类的方法、因重名而被隐藏的父类的成员变量
-
与this一样,不可在静态方法中使用
-
调用父类构造器
-
-
子类成员变量与父类成员变量类型一样名一样,则会隐藏父类成员变量
-
父类设计规则
继承的最大坏处是破坏封装
子类继承父类,子类将获得父类所有的方法和属性,如果有访问权限,可以直接拿来使用,从而造成子类和父类的高耦合
为保证父类良好的封装性,在设计父类的时候一定要遵循父类设计规则-
尽量隐藏父类内部数据
-
不让子类随意访问、修改父类方法
- 成员变量都用private修饰
- 不被访问的方法可以用private修饰
- 不想被重写但让子类访问的方法可以用public final修饰
- 希望某方法被子类访问和重写但不希望其他类自由访问可以用protect修饰
-
尽量不在构造器中使用将被重写的方法
-
-
继承和组合的使用
- 继承的两个类之间是"是"的关系
- 组合两个类之间是"有"的关系
-
类只能单继承类,但接口可以多继承接口,类可以多实现接口
多态
父类引用指向子类对象,在编译时表现为父类,在运行时表现为子类,即编译时,这个引用只可调用父类的方法,但在运行时,如果子类有重写父类的方法,那么调用这个方法总是表现出子类的行为。
-
分类
- 编译时类型
- 运行时类型
-
成员变量不存在多态,多态只可能发生在行为上
-
多态存在的必要条件
- 存在继承关系
- 存在重写
- 存在父类引用指向子类对象
-
向下转型
- 父类向子类转需要强转
- 强转后释放子类的拓展属性和方法
-
向上转型
子类型转为父类型,相当于基本数据类型当中的小转大,属于自动转换
- 子类可以自动转换 为父类
- 转换成父类以后将隐藏子类的拓展属性和方法
设计模式
高内聚低耦合
- 内聚是描述模块内的功能联系,一个好的内聚模块应当恰好做好一件事
- 耦合描述的是各个模块间的联系,耦合度是指模块间的依赖关系(调用关系、控制关系、数据传递关系)
模块间关系越多,耦合程度就越强,在面向对象思想领域,要求低耦合
各个设计模式图解
创建型设计模式
-
工厂模式三兄弟
-
反例设计
-
将创建许多对象的职责全部交给一个类,用户只需要在创建这个类的时候传相应参数
就可以得到想要的对象 -
缺点
-
里面存在大量的条件判断语句,比较影响程序性能
-
违背单一职责原则
简单工厂模式并不只是创建一个对象,也就是说并不是只做一件事,这就违背了面向对象的基本原则—'单一职责’不利于类的重用和维护(一个类有很多职责,会加大后期维护的难度)
该类指责并不单一,内部需要进行大量的判断,这就降低了创建对象的效率 -
违背开闭原则
当需要额外添加新的需要创建的对象或者是需要该类做的事情的时候需要去内部修改代码,违背了开闭原则
-
高耦合
对象的创建和使用无法分离
-
重复代码较多
-
-
-
简单工厂模式
-
设计思想
-
将需要创建的各个对象的代码封装成各个类,将这些类共同的代码封装成抽象类做他们的父类
将需要创建的各种对象的相关代码封装到各个类,称它们为具体产品类,再提取出共同代码并创建抽象类,称它为抽象产品类,每个具体产品类都继承抽象产品类
-
再提供一个工厂类并提供用于创建所需要的各个对象的方法
这个工厂用于创建不同的具体产品对象,该类中提供了创建对象的方法,只需提供相应的参数便可获得相应的对象
-
客户端只需要调用工厂类的方法并传入相应参数便可以得到想要的对象
-
-
简单工厂模式的定义
- 定义一个工厂类,它可以根据参数的不同返回不同的实例,这些实例通常有一个共同父类;
内置静态方法,专门用于获取实例,因此又叫静态工厂方法模式
- 定义一个工厂类,它可以根据参数的不同返回不同的实例,这些实例通常有一个共同父类;
-
简化
- 将工厂类与抽象父类合并,也就是在抽象父类中定义用于创建各个对象的静态方法
-
优点
- 实现了对象的创建与使用分离
- 通过改变参数就可以获取不同对象,比较灵活
-
缺点
- 工厂类职责过于繁重
- 引入工厂类,增加了类的个数,并提高代码理解难度和复杂度
- 当增加新的产品类,则必须修改工厂类内部代码,违背了开闭原则
-
适用场景
- 工厂类负责创建的对象较少
- 客户端只知道创建对象所需参数,对如何创建对象并不关心
-
-
工厂模式
即一个具体工厂只能生产一种具体产品
-
设计思想
- 针对不对的具体产品类,提供不同的工厂类
-
工厂模式的定义
- 定义一个用于创建对象的接口,让子类决定将哪一个类实例化
- 工厂模式让一个类的实例化延迟到其子类,由创建对象的工厂的各个子类决定创建哪个实例
-
角色
每个具体工厂负责一个具体产品的创建
- Product(抽象产品)
- ConcreteProduct(具体产品)
- Factory(抽象工厂)
- ConcreteFactory(具体工厂)
-
优点
-
客户端无需知道具体对象的创建细节
在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体 产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚 至无须知道具体产品类的类名。
-
基于工厂角色和产品角色的多态性设计是工厂模式的关键
它能够让工厂可以自主确 定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模 式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
-
完全符合开闭原则
在添加新的需要被创建的对象时,并不需要修改哪个类的内部代码,只需要创建新的产品类并继承抽象产品,再创建新的具体工厂继承抽象工厂,这个抽象工厂只负责这个新的产品类的创建
-
-
缺点
-
在出现新的需要创建的对象时,系统中的类会成对增加
在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统 中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行, 会给系统带来一些额外的开销。
-
-
适用场景
-
客户端不需要知道要创建的对象类,只需要知道该类对应的工厂类即可
-
抽象工厂类通过其子类来指定创建哪个对象
在工厂方法模式中,对于抽象工厂类只需要 提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和 里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
-
-
-
抽象工厂模式
即一个具体工厂用来生产一族产品
例如海尔工厂,用来生产海尔冰箱,海尔空调,海尔洗衣机产品等级结构指一种产品
-
针对属于同类的产品提供不同的工厂类,即一个工厂类负责生产一族产品
-
抽象工厂的定义
- 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类
-
包含角色
-
AbstractFactory(抽象工厂)
- 它声明了一组用于创建一族产品的方法,每一个方法对应一 种产品。
-
ConcreteFactory(具体工厂)
- 它实现了在抽象工厂中声明的创建产品的方法,生成一组具 体产品
这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
- 它实现了在抽象工厂中声明的创建产品的方法,生成一组具 体产品
-
AbstractProduct(抽象产品)
- 它为每种产品声明接口,在抽象产品中声明了产品所具有的 业务方法。
-
ConcreteProduct(具体产品)
- 它定义具体工厂生产的具体产品对象,实现抽象产品接口中 声明的业务方法。
-
-
开闭原则倾斜
-
增加产品族完全符合开闭原则
对于增加新的产品族,抽象工厂模式很好地支持了“开闭原则”,只需要增加 具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。
- 产品族指的是一组产品例如海尔公司的冰箱空调洗衣机
-
增加产品等级结构
对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽 象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。
- 产品等级结构指的是一个公司,例如增加一个电视
-
-
优点
- 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产 品族中的对象。
-
缺点
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,
这显然会带来较大的不便,违背了“开闭原则”。
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,
-
-
-
单例模式
-
目的
- 确保该类只能创建一次对象,即确保对象的唯一性
-
单例模式出现动机
例如电脑的任务管理器,不管你打开多少次,它只会出现一个窗口,且多次打开内容一致
这里就用到了单例模式
如果没有单例模式,那每次实例化这个窗口,内容不一致岂不是出现了大问题- 1.如果需要创建的很多个对象他们的内容都一样,那就没必要创建很多个,只用一个就好,可以避免系统资源浪费
- 2.如果一个系统需要的某一个类的对象每次的内容都是相等的,那每次实例化这个类不可能保证每次都相等,为了使某个类
每次创建的对象都完全一样,就需要单例模式
-
定义
- 确保一个类只有一个实例
- 自行实例化并向整个系统提供这个实例
-
设计方式
-
将该类构造器设为私有
-
在该类中定义并保存该类的唯一实例
private static 该类名 dl = null;
-
定义静态方法,供外部访问该类的唯一静态实例
public static 该类名 getInstance() {
if (tm == null) {
tm = new TaskManager();
}
return tm;
}
-
-
角色
-
Singleton(单例)
- 在单例类内部,只生成一个实例,同时提供一个getInstance()静态工厂方法,供外部访问该唯一实例
为防止外部对该类实例化,需要将其构造方法进行私有化,在类内部定义了一个Singleton类型的静态对象,
作为外部共享的唯一实例
- 在单例类内部,只生成一个实例,同时提供一个getInstance()静态工厂方法,供外部访问该唯一实例
-
-
分类
-
饿汉式单例
public static 该类名 tm = new 该类名();
public static 该类名 getInstance() {
return tm;
}-
在定义静态变量的时候实例化该类
因为是静态变量,所以一旦类被加载,该类的单例便被确定下来,且只实例化一次
-
这样不会出现并发问题
-
-
懒汉式单例
-
在调用静态工厂方法时判断单例是否为空,如果为空创建,不为空则直接返回
-
但在多线程环境中还是会出现多个实例的情况
当第一个线程调用静态工厂方法时,发现单例为空,然后它去初始化该实例,但初始化实例可能会耗费很长时间,在没有初始化完成的时候,第二个线程调用了静态工厂方法,此时单例还是为空,依旧对该类实例化…
-
在静态工厂方法上加synchronized关键字,使其每次只能被一个线程访问
-
-
-
-
原型模式
-
目的
- 为了解决每次需要创建与之前对象内容基本相同但又不是同一个对象的问题,
Sunny公司针对员工写重复的工作周报问题提出了原型设计模式,即对象的克隆
- 为了解决每次需要创建与之前对象内容基本相同但又不是同一个对象的问题,
-
定义
- 先创建一个原型对象,再通过复制创建出更多同类型的对象
-
角色
-
Prototype(抽象原型类)
- 声明克隆方法的接口,是所有具体原型类的父类
-
ConcretePrototype(具体原型类)
public Prototype clone() //克隆方法 {
Prototype prototype = new ConcretePrototype(); //创建新对象
prototype.setAttr(this.attr);
return prototype;
}- 实现抽象原型类,定义原型对象的成员变量即其他方法,并实现克隆方法,用来复制对象
-
Client(客户类)
- 在客户端类中创建原型类对象然后调用原型类对象的克隆方法创建出新的相同对象
-
-
实现
-
可以自定义抽象原型类并声明克隆方法然后再自己实现该方法
-
Object充当抽象原型类,此时只能实现原型对象的浅拷贝,即该对象内部的引用类型无法拷贝出两份
可以定义具体原型类并实现Cloneable接口,在该方法中覆盖Object的clone方法并用super调用父类clone方法class ConcretePrototype implements Cloneable {
……
public Prototype clone() {
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException exception)
{ System.err.println(‘Not support cloneable’);
}
return (Prototype )object;
}
……
}
-
-
clone方法满足
- 对任何对象x,都有x.clone() != x
- 对任何对象x,都有x.clone().getClass() == x.getClass(),即克隆对象与原型对象的类型一 样
- 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
-
-
建造者模式
结构型模式
- 适配器模式
- 桥接模式
- 组合模式
- 装饰者模式
- 外观模式
- 代理模式
- 亨元模式
行为型模式
- 命令模式
- 迭代器模式
- 中介者模式
- 访问者模式
- 观察者模式
- 策略模式
- 状态模式
- 解释器模式
- 备忘录模式
- 模板方法模式
- 职责链模式
权限修饰符
public
- 整个项目可用
protected
任何类都不能由prodected修饰
这个权限访问修饰符是面向成员变量和方法的
- 包内可用
- 包外子类可用
默认
- 包内
private
- 本类
java常用包及类
lang
-
String
-
Math
-
System
-
arraycopy
是本地native方法
-
-
Thread
-
使用lang包中的类不用使用import导包,系统自动导入
util
-
工具类
-
Arrays
-
copyOf
-
toString
底层是调用StringBulider的append方法和toString(重写Object中的toString)方法
-
binarySearch()
二分查找,在查找前必须经过排序
可以调用Arrays.sort(Object[])方法排序如果传字符串类型的数组,首先需要先将数组按字典顺序排序,然后按照字典顺序查找,如果查不到,则返回被查找的数应当放入排好顺序的字符串数组相应位置的下标+1取反
例如
String[] str = {‘a’,‘b’,‘d’};
Arrays.binarySearch(str,‘c’);
此时要查找的c应付放在数组下标为2的位置,所以返回值为-3,如果查找的字符串为e,则应返回-(数组长度+1)。
如果要查找的字符串比数组内的元素都小,则返回-1
-
-
List
-
Set
net
- 网络编程有关
io
- 输入输出流
text
- 格式化相关类
sql
- JDBC相关类
swing
- 图形用户界面
awt
- 图形用户界面
字符串
String
-
String常用方法
String底层是利用final char[]数组实现的
所以字符串是不可改变的
字符串的截取只是利用数组的复制
数组本身也不可改变
所以字符串的截取或者拼接底层都需要创建新的数组,创建新的字符串对象而StringBulider不必创建新的字符串对象,它只需要创建新数组,数组的拷贝底层都是用本地c写的拷贝方法,在大量拼接字符串的前提下,比String类型拼接效率高
-
indexOf
- 返回int下标
- 传字符或字符串
- 查找第一次出现的字符的位置
-
split
- 返回字符串
- 传入字符串
- 按指定字符分割字符串
-
codePointAt
- 返回整型
- 传整型下标
- 将指定位置字符转换成Unicode码
-
valueOf
该方法为静态方法
当传入的如果是Object,则调用Object类中的toString方法输出- 返回字符串
- 传入八种数据类型+Object
- 将传入的转换为字符串
-
getByte
- 返回byte[]
- 不传参
- 按默认编码集将指定字符串转换成字节码
-
charAt
- 返回char
- 传int下标
- 按照下标取字符
-
startWith
String s = ‘我很好’;
String s1 = ‘’;
s.startWith(s1);
返回true该方法不可传null,否则会报空指针异常
因为方法内部需要取出参数内部的char类型数组,那这就需要使用该String类实例的成员变量value,如果传空会出现null.value,就会报空指针异常
- 返回boolean
- 传int,int
- 判断该字符串是否以指定字符串开始
-
endWith
String s = ‘我很好’;
String s1 = ‘’;
s.endWith(s1);
返回true该方法不可传null,否则会报空指针异常
因为方法内部需要取出参数内部的char类型数组,那这就需要使用该String类实例的成员变量value,如果传空会出现null.value,就会报空指针异常
- 返回boolean
- 传int,int
- 判断是否以指定字符串结束
-
substring
底层是数组的拷贝
这个方法返回一个new出来的字符串
调用的构造函数里调用了Arrays里的copyOfRange方法,copyOfRange方法底层是本地c的arraycopy方法- 返回string
- 传int,int
- 截取字符串
-
equals
自反性
对称性
传递性
一致性- 返回boolean
- 传字符串
- 判断字符串是否相等
-
equalsIgnoreCase
- 返回boolean
- 传字符串
- 忽略大小写比较字符串
-
isEmpty
- 返回boolean
- 不传参
- 判断字符串是否为空字符串
-
lastIndexOf
- 返回int
- 传String引用变量
- 判断字符串最后一次出现指定子字符串的下标
-
contains
-
返回boolean
-
传CharSequence(String的父类)
这里体现出多态
-
判断字符串是否包含指定字符串
-
-
concat
- 返回String
- 传String
- 将两个字符串连接
-
length
- 返回int
- 不传参
- 返回字符串长度
-
trim
如果存在空格,则会调用字符串截取的方法,return一个new出来的新字符串
- 返回String
- 不传参
- 删除字符串两端空格
-
compareTo
- 返回int
- 传String
- 比较两个字符串在字典中的位置
-
-
为什么设计成final的
-
常量池需要
- 虚拟机对字符串是有优化的,有字符串常量池
- 如果字符串可以改来改去,那字符串常量将没有意义
-
缓存hash值
-
安全性
-
-
Java对Stirng的特殊支持
-
+、+=用于字符串拼接
- 加号操作一定出现新的String对象
-
“asa”可以直接创建一个字符串对象
这个对象会存入常量池
-
jvm对String拼接的优化
String s = ‘ab’
String f = ‘a’ + ‘b’
在虚拟机中会将上述字符串相加优化,虚拟机认为你这种相加无意义,f直接等于’ab’,且s==fString str1 = ‘12’;
String str2 = ‘aa’;
String str3 = str1 + str2;
在编译过程中,进行str1 + str2 时,先加载一个StringBuilder类,创建一个空的StringBuilder对象,然后调用appends方法,一一将str1和str2追加到创建的空StringBulider对象中,然后自动调用toString方法,将StringBuilder对象转换为String对象return出来赋值给str3.
-
-
String底层是有final修饰的char数组
-
该字符串长度不可变,只要是改变字符串的值,一定是new一个新的字符串对象
可变字符串
-
StringBuilder
-
底层是没有final修饰的char数组
-
利用数组复制实现可变字符串
底层使用System.arraycopy方法
该方法是本地c写的,效率较高 -
常用方法
-
substring
- 字符串截取
- 该方法是StringBuilder父类的方法
- 该方法与String的substring方法类似
-
append
数组追加
当传入引用类型的时候,append方法会判断,如果这个引用类型为null,那么执行appendNull方法,会将null追加到底层的char数组当中- 传8种数据类型
- 返回StringBuilder
- 字符串拼接
-
delete
-
insert(int offset, String str)
-
-
构造方法
该类的无参构造方法默认将该类中char数组定义为16长度
StringBuilder()该类有参构造方法都调用了该类的append方法,而append方法底层都是调用数组的arraycopy方法
StringBuilder(int capacity)
StringBuilder(String str)
StringBuilder(CharSequence seq) -
初始容量为16个字符
-
StringBulider类型可以直接输出
将SreingBulider类型传给println(Object obj)方法,这个输出方法调用了String.valueOf(Object)方法,这个方法又调用了toString方法,这里体现出多态,传入Sb类型到println方法中,会向上自动转型,这里最终调用的toStirng方法是StringBulider方法中重写的toString方法,最终return出去一个new String(value,0,count)类型
-
-
StringBuffer
- 跟StringBuilder本质一样
- 主要区别是StringBuilder多了线程安全
包装类
java5出现 自动装箱 自动拆箱
所有的拆箱装箱都由系统自动完成,无需程序员理会
包装类最大的优点是可以将数字类型当作引用类型用,在框架上用的比较多,可以返回null
Integer
Integer int1 = 127;
Integer int2 = 127;
Integer int3 = 128;
Integer int4 = 128;
Integer int5 = new Integer(127);
int1 == int2
int3 != int4
//只要出现new就一定是个新对象
int1 == int2 != int5;
在Integer类中,有一个256长的静态一位数组,名为cache,用来缓存-128~127的整数,
在类加载时就存入这256个Integer类型的整数,当你将这个范围之间的int类型的数赋给Integer的时候,自动装箱会使他们指向同一个对象,所以int1跟int2相等,超过这个范围,会自动创建新对象,所以int3跟int4不想等
-
valueOf
- 返回值Integer
- 传入int,String类型(必须是数字字符串)
- 将传入的转换成Integer类型
-
parseInt
- 返回值int
- 传String
- 将特定字符串转换为int
-
Integer类中有缓存区,其本质是在类中放一个静态一维数组
Byte
Short
Long
Float
Double
Boolean
Charact
- valueOf
不可变类
不可变类创建实例后,该实例的实例变量不可改变.但一定要注意引用类型,当用private final修饰的引用类型被getter访问到后,可以随意改变该引用指向的对象的实力变量,所以一定要注意getter方法的返回值,例如可以返回一个跟该引用变量的实例内部属性值相同的匿名对象
- 用final和private修饰类的成员变量
- 提供有参构造方法给成员变量初始化
- 只提供getter方法
- 必要时提供hashCode和equals
- java.lang.String和八个包装类均为不可变类 不可变类的实例变量不可被改变,可以缓存实例,下次直接拿来用,不必再创建对象
例如Integer就是缓存Integer的实例,缓存的是Integer的value值在-128到+127的实例 - enum类通常应该被设计成不可变类,使其成员变量不允许改变
泛型
集合
该集合接口为java.util包下的接口,这些在 java.util 包的集合类就都是快速失败的。
Collecttion、List、Set、Map、SortMap都是接口
快速失败:一检测到修改数据立马报异常,它是一种检测机制,即防止非并发线程在并发环境下运行
Collections
- java.util包下的提供了大量操作集合的方法的工具类
- 排序操作
- 查找替换操作
Collection
常用方法Collection接口中都有,实现其接口的类都重写了这些常用方法。若直接打印集合对象会输出[c,c,c,c],则该子类重写了toString方法
Collection c = new HashSet();
c.add(1);
-
List
父类接口中含有的方法,该类也有,另外还扩展了一些方法
LinkedList比ArrayList更占内存,因为LinkedList的每一个节点对象还包含两个医用,一个指向前节点,一个指向后节点-
ArrayList
默认长度为10
构造方法可以传int类型整数,用于指定初始长度
如果指定初始长度超过10,也没有经过扩容-
底层是Object类型的可变的数组
-
利用数组的拷贝来进行对容器内数据的操作
-
特点
- 有序
- 有下标
- 元素允许重复
- 在查找与修改某个位置的值时效率较高,元素多时在某个位置插入值时效率较低
-
常用方法
-
add()
- add(E)
- set(int,E)
-
addAll()
- addAll(Collection<> c)批量插入
- addAll(int index, Collection<> c)批量插入
-
set()
- set(E)
- set(int,E)
-
get(int)
-
remove(int index)
-
-
-
Vector
-
与ArrayList一样
-
只是比ArrayList多了线程安全,效率较低,已成为丢弃容器
-
Stack子类
线程也安全
-
栈
-
后进先出
-
API
- push入栈
- pop出栈
- peek查看栈顶元素
- empty判断是否为空
- size元素个数
-
-
-
固定长度的list
List list = Arrays.asList(‘aa’,‘b’);
- 调用Arrays的asList方法可将一个数组或指定个数的对象转为List
- 他是Arrays类中的内部类ArrayList的实例
- 只能遍历访问该集合元素,不可增删该
-
LinkedList
-
底层是数据结构中的双向链表
-
利用节点的前后指向将一个个节点连接起来组成链
-
特点
- 有序
- 伪下标(,利用循环一直.next就可以找到对应的节点)
- 元素允许重复
- 在插入时效率较高,元素多的时候查找效率低
-
常用方法
-
linkLast(E e)
-
linkFirst(E e)
-
add(E)默认在链表最后查入
-
addAll()
- addAll(Collection<> c)批量插入
- addAll(int index, Collection<> c)批量插入
-
clear()
-
get()
-
set(int,E)
-
add(int,E)
-
-
-
-
Collection接口方法
- add(Object)
- addAll(Collection)
- clear()
- contains(Obj)
- containsAll(Collection)
- iterator
- remove(Obj)
- removeAll(Collection)
- size()
- toArray()
-
Collection和Collections
- Collection接口是List、Set、Queue的父接口,实现了Iterable接口,提供了可以操作上述三个集合的方法。
- Collections是一个操作Set、List、Map等集合的工具类
-
Set
-
HashSet
当向HashSet中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据值确定该对象在HashSet中的位置。如果两个元素通过equals方法相等但他们的哈希值不等,仍然可以存入不同的位置
-
特点
- 按Hash算法来存储集合中的元素,查找插入超级快
- 无序
- 不支持排序
- 不是同步的,线程安全有问题
- 集合的元素可以为null
-
HashSet两个元素相等的标准是通过equals返回true并且哈希值相等
当将一个对象的equals方法重写后,也应该重写其hashCode方法,理由是一个元素通过equals方法比较返回true,那么他们的哈希值也应该相等。
如果将两个对象通过equals返回true,但这两个对象的hashCode方法返回的哈希值不等,那么HashSet会将这两个对象存到不同的位置,也可以添加成功,那么HashSet中的元素相当于可重复,违背了Set集合的规则。
如果两个对象通过hashCode返回的值相等,equals返回false,HashSet会试图将两个对对象以链式的方式保存到同一位置 -
LinkedHashSet子类
- 也是根据哈希值存放
- 有序
- 内部多了一个LinkedLish列表用来记录元素插入顺序
- 性能略低
- 子主题
-
当存入可变对象时,如果修改集合对象的内部成员变量,有可能使该对象与集合中其他对象相等,从而导致集合无法准确操作元素
-
-
SortedSet
-
TreeSet
采用红黑树的数据结构来存储集合元素
默认采用自然排序,并不是根据元素的插入顺序排序,而是根据元素的实际大小排序TreeSet排序规则由两种实现方式
一种是自然排序:放入集合的元素需要实现Comparable接口,该接口定义了比较方法,在将元素添加进集合的时候,集合内部会调用该元素的比较方法进行排序,这也是为什么该集合存null会出现空指针异常
另一种是定制排序,存放的元素不需要实现接口,而是需要给集合传一个比较器,Comparator,该接口是一个函数式接口,传两个参数,返回int,如果等于零两元素相等,小于0,前者小于后者,大于零前者大于后者-
直接父类是SortedSet(继承Set接口)
-
特点
-
元素有序
-
支持排序
-
元素不可重复
-
不可有null元素
因为TreeSet中需要排序,所以需要比较,比较时要调用方法,当null调用方法时会发生空指针异常
-
-
常用方法
-
first()
- 取第一个元素
-
last()
- 取最后一个元素
-
lower()
- 取指定元素的前一个元素
-
higher()
- 取指定元素的后一个元素
-
截取自TreeSet的方法
-
headSet(Object)
- 返回小于指定元素的子集
-
tailSet(Object)
- 返回大于指定元素的子集
-
subSet(o1,o2)
- 返回在传入参数之间的子集
-
-
-
如果向集合插入可变对象,并且后面程序修改了该对象实例变量,这会导致其顺序发生改变,但集合不会重新调整顺序,甚至会出现重复元素
-
自然排序
-
TreeSet会调用集合元素的compareTo(Object)方法比较元素大小进行升序排列
-
Comparable是一个接口,提供了比较大小的标准
Java一些常用类都实现了Comparable接口并实现了比较的方法
BigDecimal、BigInteger以及所有数值型包装类
Character类按字符的Unicode码比较
Boolean类中true大于false
String类型的元素按照字符串Unicode码比较
Date、Time后面的时间大于前面的时间在该方法中定义自己的排序规则,当调用
Arrays.sort(Object[] a)方法时则回调compareTo()方法,并按照自己的规则对对象数组进行排序。 -
如果试图把一个对象添加到TreeSet时必须实现Comparable接口并实现比较的方法
-
-
定制排序
- Comparator是一个函数式接口,里面包含compare(o1,o2)方法,比较并返回整数
- 在创建TreeSet对象时传入一个Comparator对象,可以写匿名内部类,也可以用Lambda表达式代替
-
-
-
Queue
-
特点
- 序列
- 先进先出FIFO
- 队列不允许随机访问队列中的元素
-
Deque接口
-
ArrayDeque实现类
-
特点
- 底层基于Array
- 双端队列
- 允许从队列两端操作队列
-
API
与Queue中的方法一样,只不过这里的方法都有两个,例如addLast,addFirst;removeLast,removeFirst
- addFirst(Object e)
- addLast(Object e)
- getLast()
- getFirst
- removeLast
- removeFirst()
-
-
-
API
-
add(Object e)
- 向队尾添加元素
-
offer(Object e)
- 向队尾添加元素
- 当使用容量有限的队列时,此方法比add更好
-
element()
- 获取队列头部元素但不删除
-
peek()
- 获取队列头部元素但不删除
-
poll()
- 获取并删除队列头部元素
-
remove()
- 获取并删除队列头部元素
-
-
-
遍历
在使用迭代器遍历元素的时候不能通过集合的方法删除元素, 否则会抛出ConcurrentModificationException 异常. 但是可以通过Iterator接口中的remove()方法进行删除.
-
普通for循环
-
foreach循环(底层还是迭代器)
-
迭代器
- 先将容器中元素放入迭代器,再根据下面两个方法遍历
-
ArrayList<String> strings = new ArrayList<>();
Iterator iterator = strings.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
- hasNext()判断是否还有数据
- next()返回元素
Map集合
HashMap与HashTable的相同与不同:
二者都实现了Map接口
继承自不同的类,前者继承AbstractMap,后者继承Dictionary,
前者可以有一个键为null,值也可为null且可重复,后者键值都不可为null且线程安全
-
HashMap
Map map=new HashMap();
map.put(null, ‘one’);
map.put(null, ‘two’);如果key一样,后放的将覆盖前放的
-
特点
- 键值对
- 采用哈希算法,查找非常快
- key可以存null
- value可以重复
- 无序
- 不支持排序
-
HashMap中是用链地址法来解决哈希冲突的
-
遍历
-
forEach
map.forEach(new BiConsumer<T t,T t1>(){
@Override
public void accept(T t, T t1) {
System.out.println(t+‘,’+t1);
}
}); -
Set>
-
HashMap集合的元素存入后实际被包装成一个Entry对象,即键值对对象,HashMap提供了一个获得所有Entry对象并放入Set集合的方法
-
-
方法
- put(T,E)
- get(key)
- keySet()返回所有key存入set集合
- replace(key, value)
- clear()
-
-
SortedMap接口
-
TreeMap实现类
-
特点
-
有序
-
支持排序
-
不可存null
-
查找较慢
-
value可以重复
-
采用红黑树排序
-
自然排序
- 按照元素的实际大小
-
定制排序
- 调用传Comparator类型的构造方法(在这直接传匿名内部类,并重写comparator方法)
-
-
-
-
-
HashTable
-
线程安全
-
key,value都不可为null
-
子类
-
Properties
-
特点
- 不可写泛型
- 键值都是String
- 可以和IO结合使用,实现永久存储
-
API
-
setProperty(k,v)
-
getProperty(k)
-
stringPropertyNames()
- 类似于Map集合中的keySet方法
-
特有方法
传任意的字节或字符流
-
读到集合
- load(InputStream in)
- load(Reader r)
-
写到properties文件
- store(OutputStream)
- store(Write)
-
-
-
-
-
-
java.util.concurrent
-
ConcurrentHashMap
-
底层原理
1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,
-
锁分段技术
- 首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问
其中一个段数据的时候,其他段的数据也能被其他线程访问。
- 首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问
-
-
Iterator和ListIterator的区别?
网络编程
网络通信协议
-
TCP/IP协议
- 应用最为广泛的协议
- 包括TCP和IP协议
-
IP地址
- 计算机在网络中的唯一标识
-
端口号
- 程序在电脑中的唯一标识,范围在065535,01024由计算机软件占用
-
InetAddress
- static InetAddress getByName() 通过主机名获取IP地址
- static InetAddress getLocalHost() 获取本机IP地址
- String getHostName() 获取主机名
- String getHostAddress() 获取主机IP地址对象
传输协议
-
UDP
面向无连接
-
无连接通信协议
-
特点
- 发送端和接收端不会建立逻辑连接:当一台计算机向另一台计算机发送数据的时候不会去管接收端是否存在
就会发送数据,而接收端接收完数据也不会给发送端反馈 - 资源消耗小、通信效率高
- 容易造成数据丢失
- 有传输大小限制,数据限制在64k以内
- 发送端和接收端不会建立逻辑连接:当一台计算机向另一台计算机发送数据的时候不会去管接收端是否存在
-
适用场景
- 新闻联播
- 视频会议
- 微信聊天、发短信
-
-
TCP
-
面向连接通信协议
-
三次握手
- 客户端向服务器端发送连接请求,等待服务器确认
- 服务器向客户端回送一个响应,通知客户端收到了连接请求
- 客户端再次向服务器端发送确认信息,确认连接
-
特点
- 资源消耗相对较大,通信效率较低
- 传输大小理论没有限制
- 比较安全,不会造成数据丢失
-
适用
- 资源下载
- 文件传输
-
UDP通信
-
DatagramPacket
- 数据报包,用于封装数据
-
DatagramSocket
- 用于发送和接收数据包的
-
发送步骤
-
创建DatagramPacket对象,封装数据,接收端IP和端口
- new DatagramPacket(byte[] b, int length, InetAddress address, int port)
-
创建DatagramSocket对象
- new DatagramSoket()
-
调用DatagramSocket对象的send方法,发送数据包
-
关闭资源
-
-
接收步骤
-
创建DatagramSocket对象,绑定端口号,要求和发送数据的端口号一致
- new DatagramSocket(int port)
-
创建字节数组用来接收数据
-
创建数据包DatagramPacket对象
-
调用DatagramSocket对象的,receive方法
- receive(DatagramPacket dp) 接收数据,数据放在数据包中
-
拆包
- 发送的IP地址
- 接收到的字节个数
- 发送方的端口号(无意义)
-
关闭资源
-
TCP通信
-
面向连接,服务器先开机,客户端连服务器
-
ServerSocket
-
用于服务器端
-
使用构造方法接收客户端连接请求,传端口号,与客户端一致
- new ServerSocket(int port)
-
使用ServerSocket对象的accept方法获取客户端Socket对象
- server.accept()
-
调用客户端Socket对象获取字节输入流,读入客户端发来的数据
- socket.getInputStream()
-
调用客户端Socket对象获取字节输出流,向客户端写数据
- socket.getOutputStream()
-
-
Socket
-
用于客户端
-
客户端使用构造方法与服务器进行连接,如果连接失败则抛异常
- new Socket(String IP, int port)
-
用客户端Socket对象获取输出流,向客户端写数据
- socket.getOutputStream()
-
用客户端Socket对象获取输入流,读服务器发来的数据
- socket.InputStream()
-
-
多线程上传图片思想:服务器没接收到一个客户端Socket,就给它开启一个线程,并将Socket对象传过去
数据类型
数据类型的本质就是为了节省空间,优化匹配
所有类型不可能都设计成定长的,例如1和999999999,那如果设计成定长的,只能让每个数据都占最长的数据的位数,这就大大浪费了内存空间
任何数据存入计算机中都是一串二进制数据,当从内存取出时,通过数据类型能更快速地匹配翻译成相应的人看得懂的数据
数据类型的转换
-
强制转换
大表述范围转小表述范围
- int自动转long
- int自动转float
- int自动转double
- float自动转double
- long自动转float
- long自动转double
-
自动转换
小表述范围转大表述范围
-
特例
常量赋给表数范围小于int的类型的值自动转换
前提是这个常量的值在其表述范围内
基本数据类型
整数默认为int类型
小数默认为double类型
Java中所有的类型最终都会转换为8种基本数据类型
-
byte
一字节,8位
-27—27-1
默认值0 -
short
2字节16位
默认值0 -
int
4字节32位
默认值0- 十进制数在内存中以补码形式存在
-
long
8字节64位
定义时需要加L
默认值0- Date时间的数据类型底层是long类型
-
char
2字节16位
默认值空格
表述范围 0~65535
0(216-1)-
char c = ‘\u1234’;
编译通过且能正确执行
-
char c = 2 + ‘2’;,那么c=4
超过9将变成字符
-
编译成功,单引号中只能是四位数字,不能多也不能少
-
-
float
4字节32位
定义时需要注意:float f = 2.03F
默认值0.0 -
double
8字节64位
默认值0.0 -
boolean
1字节或4字节
默认值false
引用数据类型
引用数据类型变量存放的是对象的地址,这个变量指向对象,引用类型之间赋值是赋地址
默认值为null
- 类
- 接口
- 数组
流程控制语句
分支语句
-
if-else
-
switch-case
-
支持可以自动转int的类型
-
支持String类型
- JDK1.7版本及以后支持
-
支持枚举
-
循环语句
-
for
-
while
-
do-while
-
foreach
- 底层就是迭代器
-
循环关键字
-
break
结束当前层循环
-
continue
结束当前层该次循环
-
return
结束方法
-
标识符
标识符定义
- 类名
- 变量名
- 方法名
- 等等统称
标识符规则
- 不能数字开头
- 不能含空格
- 不可有特殊符号
标识符组成
- 数字
- 字母
- 下划线
- $
运算符
算数运算符
比较运算符
逻辑运算符
三目运算符
拓展运算符
赋值运算符
位运算符
-
<<
-
&
-
|
-
~
-
^
数组
声明与初始化
int[] arr = {4,3}
int[] arr = new int[2]
int[] arr = new int[]{4,3}
特点
- 长度不可变
- 存放类型一定
操作
由于数组长度不可变,所以数组的复制类操作都需要创建新数组
-
复制
- 扩容
- 剪切
-
遍历
相关问题
-
排序
冒泡排序
两次循环
第一次循环控制找几次最大值,
第二次循环找最大的放最后
选择排序
插入排序
堆排序 -
查找
二分查找
线性查找 -
求值
求最大值最小值
变量
成员变量
-
定义在类全局
-
有默认值
- 数字默认为0
- 引用类型默认为null
- 布尔默认false
-
系统不会给final修饰的成员变量初始化
如果直接调用没有显式赋值的final修饰的成员变量,则会出错,但是通过方法调用,此时变量的值为0,这是Java设计的一个缺陷
局部变量
局部变量没有默认值,不可被使用,
- 仅在该代码块可用
- 无默认值
- 系统不会对局部变量初始化
构造方法
用来创建对象
创建任何对象,都是从该类所在的继承树的最顶层的类的构造器一次向下执行,直到执行到该类构造器。
一般隐式调用父类无参构造器
但是如果父类是有参构造器,此时无参构造器将不存在,父类必须显示调用父类有参构造器
方法名
返回值
- 不用写返回值类型
- 返回值为该类的对象
- 不写返回值不是没有返回值
参数列表
-
无参
无参构造方法是类中默认的构造方法,
当创建了有参构造方法以后,无参构造方法将失效。
通过无参构造方法创建成员变量为默认值的对象 -
有参
通过有参构造方法可以给对象的成员变量赋初始值
重载
- 方法名相同
- 参数列表不同
new调用该方法创建对象
构造方法跟普通方法相似,都需要被调用,构造方法由new关键字调用,只是不用’.',直接空格就可以
构造方法必须调用父类构造方法
任何构造方法必须调用父类构造方法,如果不写,默认调用父类无参构造方法,先执行父类构造方法,再执行子类构造方法
方法
参数的传递
-
值传递(call by value)
在java中,传递参数时只是把实参的副本赋给形参,改变副本不影响原来的值;Java中,不管是值传递还是址传递,实质上都是值传递
-
址传递(call by reference)
址传递时发生在引用类型的参数传递上,实质上也是将引用类型的副本赋给形参,但此时赋给形参的是他所引用的对象的地址,所以形参跟实参指向同一个引用,执行方法以后,这个引用的对象可能会因方法的执行而改变,从而造成实参引用改变。表面上,我们可以把引用类型的参数传递理解为址传递。
方法重载
发生在同一个类中:方法名相同,参数列表不同
-
确定方法的三要素
- 调用者
- 方法名
- 形参列表
形参个数可变的方法
public static void test(int a,String…books);
个数可变的形参只能处于形参列表的最后
一个方法中只能包含一个个数可变的形参
个数可变的形参的实质是传入一个数组
方法三要素
- 返回值类型
- 方法名
- 参数列表
方法所属性
-
类方法
- 属于该类本身
- 不经过对象
-
普通方法
- 属于该类对象
方法默认主调者
- 静态方法默认由类直接调用
- 普通方法由该类对象默认调用
抽象方法
抽象方法一定不可以写大括号,大括号里不写东西也不行
-
默认由public abstract修饰
也可以不写public,但abstract必须有
不写public为默认权限 -
只有方法签名,没有方法体
-
不可被调用
-
必须被重写,重写规则在本思维导图的面向对象三大特征的继承中
抽象
final和abstract永远不可能同时使用
接口和抽象类的区别
接口只能包含抽方法、静态方法、默认方法(Java8新特性)和私有方法(Java9新特性),抽象类可以包含普通方法
接口只能定义静态常量,抽象类可以定义普通成员变量和静态常量
接口不含构造器,抽象类可以有构造器
接口不含初始化块,抽象类可以有
一个类只能继承一个类包括抽象类,但接口能多继承接口
抽象类
设计理念:is-a,强调从属关系
-
包含元素
- 可以包含普通方法和抽象方法
- 成员变量
- 初始化块
- 内部类
- 含有构造器,但是不是用来实例化对象的,是用来供给子类调用的
-
特点
- 用abstract关键字修饰类
- 抽象类不可被实例化
- 抽象类不可被private和protected修饰
- 抽象类可以继承抽象类
- 抽象类可以实现接口
- 主要作为多各类的模板
-
abstract
-
static和abstract不能同时修饰方法
用static修饰的方法属于类,可以用类直接调用,用abstract修饰的方法没有方法体,用这两个词修饰一个方法,那么在类调用该方法时会出现错误,因为类调用了一个没有方法体的方法。
但static和abstract可以同时修饰内部类 -
final和abstract不能同时修饰方法
用abstract修饰的方法就时用来被重写的,但用final修饰的方法不可被重写,就会使该抽象方法变得没有意义
-
接口
设计理念:like-a,强调功能
-
接口定义
- 接口是比抽象类更抽象的类
- 不再使用class关键字,而是使用interface
- 定义多个类应该遵守的规范
- 不可被实例化
- 不能被final和protected修饰修饰
- 一个类可以实现多个接口
- 接口可以多继承接口
-
接口中元素
-
接口不包含构造方法
-
接口中只能定义静态常量,不可定义普通成员变量
静态常量必须赋初值
-
接口中不能声明引用
-
Java7
只能包含两种元素:成员变量、抽象方法
- 成员变量只能是静态常量
- 抽象方法
-
Java8新特性
在Java7的基础上新增特性
-
可以在接口中定义默认方法
默认方法必须写方法体
默认方法必须用default修饰,默认方法不能用static修饰,总是用public修饰,如果不写,默认也是用public修饰。可以用接口的实现类的实例来调用默认方法 -
可以在接口中定义静态方法
静态方法必须写方法体
静态方法默认由public修饰,不能用default修饰,
-
-
Java9新特性
在Java8的基础上新增特性
- 可以在接口中写私有方法
- 可以在接口中写私有静态方法
-
static
只能修饰属性、方法和初始化块
由它修饰的属性和方法属于类本身,不属于对象
由它修饰的属性或方法,该类所有对象共享,在内存中仅有一份
static修饰的在类加载时就初始化了
static修饰的可以直接用类名调用
不能使用this
静态代码块在类加载时初始化,并且只加载一次
第一次使用到该类时加载类
final
可以修饰普通类、方法和属性
用final修饰的变量不可被二次赋值
用final修饰的类不可被继承
只是不能被继承,只要构造方法是公共的,就可以创建对象
final修饰的方法不可被重写
- 重写final修饰的方法会发生编译错误
final修饰的成员变量必须指定初始值
-
由final修饰的类变量
必须在静态初始化块中指定初值或声明该类变量的时候指定初值,但只能在这两个地方的其中之一指定
-
final修饰的实例变量
必须在非静态初始化块中、声明该实力变量时或构造器中指定初始值,而且只能在这三个地方的其中之一指定
final修饰的成员变量不允许在显式初始化前直接访问,但允许通过方法访问
这是java设计的一个缺陷
final int i;
{
//下面代码将引起错误,此时i未初始化
System.out.println(i);
//通过方法就可以访问到
test();
//当i显示初始化以后就可以直接访问
i = 4;
System.out.println(i);
}
void test(){
System.out.println(i);
}
修饰局部变量
public void test(final int i){
//不可在这里给i赋值
//i= 8;
}
public static void main(String[] args){
//可以调用方法传参
new Final().test(1);
new Final().test(3);
}
final修饰引用变量
-
该变量不可被二次赋值
-
该变量引用指向的对象的内部可以发生改变
//只是不能再次赋给arr新的数组,但是数组
//内部的东西可以随意改变
final int[] arr = {5,6,4};
arr[0] = 6
宏变量
不管是实例变量还是局部变量还是类变量,只要满足这三个条件都会相当于一个直接量
直接量会缓存在常量池中,第一次出现直接量,会放入常量池,例如 String s = ‘a’;
这时会把’a’放入常量池,下一次再执行String b = ‘a’;则会让b直接指向常量池中的’a’
-
被final修饰
-
在编译时,初始值就被确定
意思就是该变量直接被赋值为常量,或者是简单的字符串常量相加,没有调用到方法或者没有使用无final修饰的变量,那么该变量在编译时就被确定了初始值
final String str1 = ‘a’;
final String str2 = ‘b’;
final String stri = ‘ab’;
final String str3 = str1 + str2;
//该语句返回true
System.out.println(str3 == stri);若str1和str2没有被final修饰,则str3则不能在编译时就被确定下来
-
只有在定义该变量时指定其初始值才有宏变量的效果
初始化块
如果两个构造器中有相同的初始化代码,且这些代码无需接收参数,此时可以把这些代码放入初始化代码块中
顺序
从树顶端开始,依次执行静态初始化块,执行完以后再从树顶端开始,先执行父类的普通初始化块,执行构造方法,再执行子类的初始化块,执行构造方法。
- 前面定义的块先执行
- 后面定义的块后执行
- 先执行静态后执行普通
可包含
- 局部变量
- 调用其它对象的方法
- 控制语句
静态初始化块
- 只能被static修饰
- 类加载时执行
普通初始化块
- 创建对象时执行
枚举
在某些情况下,一个类的对象是有限且固定的,比如季节类只有四个对象,行星类目前只有8个对象。这种实例有限且固定的类,在Java中被称为枚举类
从java5增加了枚举类
枚举的本质是用enum修饰的final类
抽象与非抽象
- 非抽象枚举类默认由final修饰但不能显示使用final修饰该类
- 抽象枚举类不能用final修饰,并且默认用abstract修饰且不能显示使用abstract修饰该类
- 枚举类里实现抽象方法,每个枚举值分别实现抽象方法,语法与匿名内部类大致相似
枚举也是一个类,在编译后,也会生成.class文件,枚举是一个特殊的类
枚举类可以实现多个接口
-
如果按照普通类实现接口那样,重写接口中的方法,那么枚举类中所有实例的行为就会相同
-
枚举类实现接口,应该让每个枚举值分别实现接口中的方法
语法如下:
MALE(‘男’){
public void info(){
System.out.print(‘男生’);
System.out.print(‘重写方法’);
}
},
FAMALE(‘女’){
public void info(){
System.out.print(‘女生’);
System.out.print(‘重写方法’);
}
}; -
这样写实质上是写的枚举的匿名内部子类,在编译时也会生成匿名子类的.class文件
包含元素
-
成员变量
-
枚举值
枚举值是静态常量,默认由
public static final修饰 -
方法
-
构造器
一旦定义了带参构造器,那么在列出枚举值的时候就要传入相应的参数
与普通类的区别
-
构造器只能用private修饰
-
默认继承java.lang.Enum抽象类
Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口
-
默认使用final修饰不能被继承
-
枚举类的所有实例在第一行列出,并且默认用public static final修饰
-
枚举不可以显示继承任何类
Enum提供的方法
枚举类默认继承Enum,所有枚举类都可直接调用Enum类中的开放权限的方法
-
values()
- 获取枚举类的所有实例,返回一个该类类型的数组
- 该方法为静态方法
- 该方法是在编译期做的事情,在api中没有介绍
-
toString()
- 实例方法
- 返回枚举常量名称
- 正是由于这个方法,所以可以直接输出枚举类型的常量名
-
String name()
- 返回枚举实例名称
- 与toString方法功能相同
异常
异常的定义及概念
-
异常是用来处理可能因很多原因出现的以外状况的
-
异常的五个关键字
-
try
-
这个块中写可能发生异常类代码
-
与catch搭配使用
-
如果try中某一行代码发生异常,那么此后的代码将不执行
仅是try块中发生异常处之后的代码不执行,跟try块外无关
-
-
catch()
- 这个块中写发生异常后的处理方法
- 传一个异常类型的参数
- 一个try后可以跟多个catch,当发生异常时根据参数找catch块
-
finally
-
在try-catch最后
-
一般在这个块中关闭在try块中打开的资源
-
如果try中有return,那么会先执行finally中的语句,最后return
try {
i = 5;
throw new NullPointerException
} catch(NullPointerException e) {
e.printStackTrace();
//此时将i=5暂存在这里
//意思就是return 5先不执行
return i;
} finally {
i++;
//输出6,并反身去执行catch中的return
System.out.println(i);
}上述代码return出的i依旧是5
-
不管有没有异常,这个块中的程序都会执行,除非整个程序结束
-
不要在此块中使用return、throw关键字,一旦使用,try块和catch块中的rerturn和throw将失效
-
-
throws
- 写在方法签名处,向调用该方法的上级抛异常,声明抛出异常的类
- 后面跟异常类
-
throw
例如:
throw new NullPointerException(String);- 抛出一个具体的异常,是一个独立的语句,后面跟异常的一个对象
-
Java非正常情况有两种
-
错误(Error)
Error的派生类也属于运行时异常
-
此错误无法处理,发生的可能性小,所以不需要处理机制
-
发生场景
- 系统崩溃
- 内存溢出
- 动态连接失败
-
-
异常(Exception)
-
可处理异常
-
发生场景
-
异常的分类
-
编译时异常
-
Checked异常
-
该类异常在编译阶段必须被处理,否则编译错误
try-catch-finally捕获处理
throws抛出异常
-
-
运行时异常
所有RuntimeException类或其子类的异常都是运行时异常,其他类的异常都是Checked异常
- Runtime异常
- 该异常在编译时无需处理
-
-
Throwable
-
Error
- AWTError
- IOError
- LinkageError
- ThreadDeath
-
Exception
-
RuntimeException
-
NullPointerException
当出现null.XXX一定会发生空指针异常,不管是调用方法还是调用属性
-
IndexOutOfBoundsExceptipn
- StringIndexOutOfBoundsException
- ArraysIndexOfBoundsException
-
ClassCastException
-
IllegalArgumentException
-
ArithmeticException
例如
int i = 2;
int j = 0;
System.out.print(i / j); -
NumberFormatException
例如
//当把字符串转成int时,如果传参不是数字
//类型的字符串,会出现此异常
Integer.parseInt(‘asd’);
-
-
IOException
-
SQLException
-
处理异常的方式
-
try-catch-finally
原理:在try中的代码如果有异常,系统会自动生成一个异常对象,将这个异常对象给运行环境,运行环境会根据异常的类型去try后面的catch找,跟catch的参数一一比对,如果找到了,会执行该catch中的代码,如果没找到,则运行环境会终止,java程序也会退出,这个过程称为捕获
-
异常处理可以嵌套,但会降低执行效率不建议使用
-
try块catch块遇到return、throw都会结束,然后直接奔向finally
-
Java7新增多异常捕获
try{
…
}catch(…|…|…)- 捕获多异常时,异常变量有final隐式修饰
-
-
throws
原理:当前方法不知道如何处理这种类型的异常,该异常会应该由上一级调用者处理,throws就是将异常向调用者抛。
- 只能在方法签名中使用
- 后面跟异常类
- 可以抛出多个异常类,用,隔开
- 一旦是使用了抛出异常,就不能继续捕获异常,只能由调用者捕获或者继续上抛
-
throw自行抛出异常
-
直接抛出一个异常的实例,可以当作独立语句使用
-
在catch块中结合throw使用
先捕获一部分异常,然后继续自行向方法调用者继续抛出异常,然后方法调用者再次捕获异常
例:public static void test(String string){
double d = 2.0;
try {
d = Double.parseDouble(string);
} catch (NumberFormatException e) {
throw new ClassCastException(‘非数字字符串不能强转为double类型’);
}
}
public static void main(String[] args) {
try {
test(‘sdsd’);
} catch (ClassCastException e) {
System.out.println(e.getMessage());
}
}
-
所有异常类的通用方法
-
getMessage()
- 获取异常详细信息的字符串,包括发生异常的原因和位置
- System.out.print(e.getMessage())
-
printStackTrace()
打印出异常原因和位置
- 打印异常错误和位置以及异常跟踪栈信息
-
getStackTrace()
- 返回跟踪异常栈信息
自定义异常
- 继承异常类,然后根据异常的处理方式抛出异常
IO
文件本质都是字节
路径
-
反斜线\
-
在双引号中为\
在双引号中\需要用转义字符标记,所以为\
-
绝对路径
一直到磁盘的根为绝对路径
- 优点:在任何项目中使用绝对路径绝没问题
- 缺点:没有系统移植性
-
相对路径
相对于当前java文件的路径叫相对路径
File
文件和目录路径的抽象表示形式
该类用于对文件和目录进行操作,但不能访问文件本身内容
-
特点
- File可以创建删除重命名文件和目录(目录就是文件夹)
- File不可以访问文件本身的内容
-
常用方法
-
exists()判断文件或目录是否存在
-
isFile()判断File对象对应的是否是文件而不是目录
如果File对象所对应的文件存在并且不是目录,则返回true,否则返回false
-
isDirectory()判断对象是否是目录而不是文件
当且仅当该抽象路径存在并且是目录,返回reue,否则返回false
-
createFile()
当此File对象对应的文件不存在时将创建一个新文件,创建成功返回true,否则返回false
-
delete()删除对象对应的文件或空路径,如果想要产出一个文件夹下所有东西,
包括这个文件夹下的目录文件与子目录及其文件,可以使用递归,如左侧代码 -
mkdir()创建目录,但需要有父目录
-
mkdirs()创建目录不需要父目录存在
-
getName()以字符串形式返回文件或目录名
如果是路径,返回最后一级路径
-
list()
- 获取路径下所有子目录和文件的名,返回String数组
-
listFile()
- 获取File对应的路径下的目录和文件的全路径,返回File数组
-
-
过滤器
-
FileFilter
-
方法accept(File pathname)
- accept的参数市listFile方法传递过来的
- 在accept方法中,进行判断,如果满足自己要求,则将true返回给listFile方法,此时listFile将
此时拿到的全路径保存到File数组中 - 若拿到的路径不满足自己的要求,则返回给listFile方法false,此时不保存该路径
-
-
listFile(FileFilter接口实现类)
- 该方法在获取到文件全路径的同时,会去调用过滤器的accept方法,并将全路径作为参数传给accept方法
-
-
遍历多级目录需要使用递归
流的分类
-
按方向
-
输入流
将文件读入到内存称为输入流
- 只能从中读取数据而不能写
-
输出流
将内容从写入到文件称为输出流
- 只能向其写数据而不能读
-
-
按功能
-
字节流
-
操作的数据单元是8位的字节
-
主要由InputStream和OutPutStream作为基类
他们都是抽象类,无法直接创建实例
-
-
字符流
-
操作的数据单元是16位的字符
-
主要有Reader和Writer作为基类
他们都是抽象类,无法直接创建实例
-
字符流可以将字符串作为物理节点
-
-
对象流
-
-
按角色
-
节点流
构造器参数是物理节点的流
- 低级流
- 程序直接与数据源连接
-
处理流(也称包装流)
构造器参数不是物理节点,而是已存在的流
- 高级流
- 程序与数据源不直接连接,通过处理流包装已存在的流进行数据处理
-
IO的四十多个类的抽象基类
-
所有输入流基类
-
InputStream
字节输入流
-
方法
-
int read()
- 读取一个字节数据,返回这个字节数据的十进制整数值
-
int read(byte[] b)
- 读取一组字节数据并放入byte类型的数组中
- 返回读取的字节数
-
int read(byte[] b, int off, int len)
- 读取一组字节数据放入byte数组中
- 从off开始
- 读取len个字节放入数组
- 返回读取的字节数
-
-
FileInputStream->InputStream
-
-
Reader
字符输入流
-
方法
-
int read()
- 读取一个字符数据返回这个字符的int值
-
int read(char[] c)
- 读取一组字符并放入char类型的数组中
- 返回读取的字符数
-
int read(char[] c, int off, int len)
- 读取一组字符放入char类型的数组中
- 从off开始
- 读取len个字符
- 返回读取的字字符数
-
-
FileReader->InputStreamReader->Reader
FileReader继承自InputStreamReader,前者只是字符输入流,其构造函数参数只能传物理节点或字符串代替物理节点。
InputStreamReader可以将字节输入流转换成字符输入流
-
-
-
流的关闭
- void close()
- 保证流的物理资源被收回
- 保证缓冲当中的数据flush到数据节点即输出目标中
-
所有输出流基类
-
OutputStream
输出字节流
-
write(int c)
- 将指定的字节输出到流中
-
void write(byte[] b)
- 将指定字节数组输出到指定流
-
void write(byte[] b, int off, int len)
- 将指定字节数组输出到流
- 从off开始
- 输出len个字节
-
FileOutputStream继承自OutputStream
-
-
Writer
输出字符流
-
write(int c)
- 将指定的字符输出到流中
-
void write(byte[] b)
- 将指定字符数组输出到指定流
-
void write(byte[] b, int off, int len)
- 将指定字符数组输出到流
- 从off开始
- 输出len个字节
-
void write(String s)
- 将字符串输出到指定流
-
void write(String s,int off, int len)
- 将制定字符串输出到指定流
- 从字符串的off开始输出
- 输出len个字符
-
FileWriter->OutputStreamWriter->Writer
FileWriter继承自OutputStreamWriter,前者只是字符输出流,其构造函数参数只能传物理节点或字符串代替物理节点。
OutputStreamReader可以将字节输出流转换成字符输出流
-
-
处理流
它可以隐藏底层设备上节点流的差异,并对外提供更加方便的输入输出方法,更加便捷
处理流又称包装流是因为处理流采用了装饰者模式,将底层节点流包装成用起来既简便又强大的流
-
特点
- 只要构造器中传的不是物理节点而是一个已有的节点流,那这个流一定是处理流
- 处理流的关闭只需要将最开始定义的处理流关闭即可,系统会自动关闭被该处理流包装的节点流
-
常见处理流
-
打印流
-
特点
- 次流不负责数据源,只负责数据目的
- 为其他流添加功能
- 永远不会抛出IOException,但会抛出别的异常
- 两个打印流的方法是一致的
-
PrintStream
字节打印流
-
PrintWriter
字符打印流
-
-
转换流
将字节流转换为字符流或者将普通流转换为高级流
File file = new File(‘F:\IO\target.txt’);
try {
//字节输入流,以file作为物理节点
FileInputStream fileInputStream = new FileInputStream(file);
//将字节输入流转换为字符输入流
InputStreamReader bufferedInputStream = new InputStreamReader(fileInputStream);
//将字符输入流包装,可以更加方便使用,主要可以直接读一行
BufferedReader bufferedReader = new BufferedReader(bufferedInputStream);
String line = ‘’;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}-
InputStreamReader
将字节输入流转换为字符输入流
- 参数传字节输入流
- 将字节输入流转换为字符输入流
- 该类父类为Reader
-
OutputStreamWriter
将字节输出流转换字符输出流
- 参数为字节输出流
- 将字节输出流转换为字符输出流
- 该父类为Writer
-
BufferedReader
-
字符输入流
-
功能更强大
-
主要优点
- 提供readLine()方法每次读一行
-
继承自Reader
-
-
BufferedWriter
- 字符输出流
- 带缓冲
- newLine()写入一个换行符
- 继承自Writer
-
-
序列化
所有可以通过网络传输的对象的类都应该是可序列化的,否则会出现异常.
Remote Method Invoke 即远程方法调用RMI,
序列化是RMI过程的参数和返回值都必须实现的机制,而RMI又是JavaEE技术的基础——所有的分布式应用常常需要跨平台、跨网络,所以要求所有传递的参数返回值都必须实现序列化。因此序列化机制是JavaEE的基础。通常建议,程序中创建的每个JavaBean类都实现Serializable。
-
序列化的目的
-
序列化的意义
- 使得对象可以脱离程序的运行而独立存在
-
什么是序列化
-
对象的序列化指将一个Java对象写入IO流
- 方法不会被序列化,序列化的仅仅是数据
-
对象的反序列化是从IO流中恢复Java对象
-
-
如何实现可序列化
-
实现Serializable接口
- 实现该接口不需要实现任何方法
-
实现Externalizable接口
-
-
对象流实现序列化
-
首先该对象的类实现可序列化接口
-
静态不是对象的一部分所以不参与序列化
-
序列化步骤
- 1.创建ObjectOutputStream,该输出流是一个处理流
- 2.调用该处理流的writeObject()方法输出可序列化对象
- 当一个可序列化类有多个父类时,(包括直接父类和间接父类),这些父类要么有无参构造器,要么是可序列化的
否则在反序列化时将抛出InvalidClassException异常,如果父类不是可序列化的,只是带有无参构造器,那么在
序列化时,父类中定义的成员变量的值不会序列化到二进制流中
-
反序列化
- 1.创建ObjectInputStream流,该流同样是处理流
- 2.调用处理流的readObject()方法读取流中的对象,如果知道对象的类型,可以强转
- 反序列化读取的是Java对象的数据而不是Java类,因此采用反序列化恢复对象时必须提供Java对象所属的class文件
如果找不到,则发生类找不到异常。另外,反序列化无须调用构造函数来初始化Java对象。 - 使用序列化机制向文件写入多个Java对象,使用反序列化恢复对象时必须按照写入顺序进行读取
-
反序列化对象时,不会调用该对象的构造函数,但如果该对象所属类有父类,且父类是不可序列化的,则会调用父类无参构造函数
如果父类是可序列化的,则不会调用
-
-
对象引用序列化
-
当一个类中的成员变量是一个引用类型,那这个引用类型必须是可序列化的,否则,不管你实现还是
不实现Serializable接口,你的类都不可以实现序列化 -
特殊序列化机制
- 1.所有保存到磁盘的对象都有一个序列化编号
- 2.当程序试图序列化一个对象时,程序将先检查该对象是否已被序列化,只有该对象从未被序列化的时候,才会被序列化
- 3.如果对象已经被序列化过,那么程序将只是输出一个序列化编号,而不是再次重新序列化该对象
- **当反序列化两次一个对象时,他们返回的对象用==判断则返回true
-
注意
- 当程序序列化一个可变对象时,只有第一次使用writeObject()方法输出时才会将该对象转换成字节序列并输出,
当程序第二次调用方法输出,程序只是输出前面序列化编号,即使后面对象实例变量值已经改变,改变的实例变量也不会
被输出
- 当程序序列化一个可变对象时,只有第一次使用writeObject()方法输出时才会将该对象转换成字节序列并输出,
-
-
自定义序列化
-
递归序列化
- 序列化一个对象,若它的成员变量引用到另一个对象,那也会序列化引用到的对象
引用到的对象的成员变量又引用到另一个对象,则也会序列化这个对象
- 序列化一个对象,若它的成员变量引用到另一个对象,那也会序列化引用到的对象
-
transient
- 只能修饰实例变量
- 作用是让序列化忽略该实例变量
- 该变量称为瞬态实例变量
-
特殊签名的方法
在对象中定义这些特殊签名的方法,在调用处理流对这些对象进行序列化个反序列化时,会自动调用这些方法,从而会运用自己的逻辑实现序列化和反序列化
- readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
- writeObject(ObjectOutputStream out) throws IOException
-
-
序列化注意事项
- 对象的类名、实例变量(包括基本类型、数组和对其他独享的引用)都会被序列化;方法、类变量,transient都不会被序列化
- 实现Serializable接口的类如果需要让某个实例变量不被序列化,用transient关键字,而不是static,虽然static可实现此效果,但不合理
- 保证序列化对象的成员变量类型也是可序列化的,否则使用transient关键字,不然这个类不可序列化
- 反序列化对象时必须有序列化对象的class文件
- 当通过文件、网络来读取序列化对象时,必须按照实际写入顺序读取
-
序列号冲突问题
- 在编辑器将java文件编译成class文件的同时,如果这个类可序列化,则给这个类计算序列号,第一次序列化该类
对象时,按照该序列号为准,当序列化以后改变了类的内容,则编辑器重新编译且生成新序列号,此时再反序列化
则会出现序列号冲突,反序列化异常 - 给该可序列化类定义序列号:格式要求static final long serialVersionUID = 222L
- 在编辑器将java文件编译成class文件的同时,如果这个类可序列化,则给这个类计算序列号,第一次序列化该类
NIO
线程
如果说异步是一个目的地,那么可以把线程当作通往目的地的车,即线程的意义就是实现异步
异步就是当A发起一个操作后可以去干其他事情,不用等待这个操作执行完成
多线程的意义就在于最大限度地利用CPU资源
线程的本质
- 线程本质上是进程中一段并发操作的代码,所以线程需要操作系统投入CPU资源来运行和调度
线程和进程
-
进程
-
一个程序进入内存运行时,就变为一个进程
-
进程是操作系统进行资源分配和调度的一个独立单位
-
进程的三个特征
-
独立性
- 是系统中独立存在的
- 拥有自己私有的地址空间
- 拥有自己独立的资源
-
动态性
- 进程拥有自己的生命周期和各种状态
-
并发性
-
多个进程可以在单个处理器上并发执行,各个进程之间不会相互影响
-
并发性与并行性
- 并发性是同一时刻一条指令在单个处理器上执行,由于处理器快速轮换,就好像多条指令同时执行
- 并行性是同一时刻多条指令在多个处理器上同时执行
-
-
-
-
线程
- 当一个程序运行时,内部包含多个顺序执行流,每个顺序执行流就是一个线程
- 线程是进程的基本单位
- 每个进程包含一个或多个线程
- 同一个进程内的线程独立运行但资源共享
线程的创建方式
-
继承Thread类
- 1.定义Thread的子类并重写run()方法,该方法就是线程执行体
- 2.创建Thread子类的实例,该实例就是线程对象
- 3.用该实例调用start()方法就启动了一个线程
-
实现Runnable接口
- 1.定义Runnable接口的实现类,并重写run()方法,该方法就是线程执行体
- 2.创建实现类的实例,并以此实例作为Thread的target来创建Thread对象
- 3.用Thread对象调用start()方法启动线程
-
使用Callable和Future创建
-
Callabe接口
-
call()方法
- Callable接口提供了call()方法作为线程执行体,该方法可以有返回值
- 该方法可以声明抛出异常
-
该接口不是继承自Runnabl接口,所以不可作为Thread的target
-
Callable有泛型限制,且泛型与call方法的返回值类型一致,Callable是函数式接口,可以用lambda表达式表示
-
-
Future接口
-
提供一个FutureTask实现类,该类实现了Future、Runnable接口,可以作为Thread的target
-
让FutureTask与Callable关联,来代表Callable接口中call()方法的返回值,并作为Thread的target
-
提供的方法
- cancel()
- get()
- isDone()
- isCancelled()
-
-
创建线程步骤
FutureTask f = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
return 1;
}
});
new Thread(f,‘有返回值’).start();
System.out.println(f.get());- 1.创建Callable接口的实现类并实现call()方法,该方法作为线程执行体
- 2.使用FutureTask类来包装Callable对象,该FutureTask对象封装了call()方法的返回值
- 3.使用FutureTask对象作为Thread对象的target创建并启动线程
- 4.调用FutureTask对象的get()方法来获得返回值
-
线程的状态
-
新建状态
- new一个线程类
- 只由JVM给其分配内存空间,初始化其成员变量
-
就绪状态
- 当一个新建线程调用start()方法,该线程由新建状态变为就绪状态
-
运行状态
- 当就绪状态的新线程获得cpu,该线程变为运行状态
-
阻塞状态
- 当运行状态的线程遇到IO请求或者调用sleep()方法,线程变为阻塞状态
- 线程在等待某个通知(notify)
- 程序调用了线程的suspend()挂起方法,该方法容易导致死锁
-
死亡状态
进入死亡状态的线程,它还可以恢复,GC不会回收
- 线程执行体执行完成正常结束变为死亡状态
- 线程调用stop()方法,线程变为死亡状态
线程的控制
-
join线程
在一个线程执行过程中遇到join线程,则正在执行的线程会进入等待状态,也是阻塞状态,知道join线程执行完成,该线程才会由阻塞状态变回执行状态
- join()
-
后台线程
- setDaemon(true)可以将前台线程设置为后台线程
- isDaemon()判断线程是否为后台线程
- 当前台线程全部死亡,后台线程也会直接死亡
-
线程睡眠
- sleep(int 毫秒)
-
改变线程优先级
- setPriority(int priority)
- getPriority()
线程同步
-
线程安全问题
当两个线程同时访问并修改资源,会造成安全问题
比如两个人并发对一个银行账户操作,就会出现安全问题,例如没钱也可以取钱 -
同步代码块
-
把对共享资源的操作的代码放在同步代码块中,使其任何时刻只能有一个线程访问该代码块
-
语法格式
-
synchronized(obj){…}
obj为同步监视器,该关键字锁的是一个对象
-
-
-
volatile关键字
-
只能修饰成员变量
-
可以称之为轻量级synchronized
-
两大性
-
可见性
- 即一个线程将其改变,其他线程立马可以知道
-
有序性
- 指程序中上下两条无关的代码执行不会被指令重排序
- 指令重排序是指在运行程序时,处理器为了优化性能,代码的执行顺序可能不会按代码的顺序执行
-
-
-
同步方法
-
用synchronized关键字修饰方法
实例方法不需要显式指定同步监视器,同步方法的同步监视器是this,也是调用该方法的对象
-
原子性
被保护的代码只能被一个线程访问
-
可见性
执行完该关键字保护的代码块后,修改的变量对其他线程是可见的
-
有序性
-
-
-
同步锁lock
Lock是一个接口
-
创建锁对象
锁对象.lock()//加锁
.
.
省略代码
.
.
锁对象.unlock()//开锁 -
每次只能有一个线程对Lock对象加锁,访问共享对象之前应先获得Lock对象
-
-
悲观锁
-
对应现实生活中悲观的人,即什么事都想的比较坏。在进行数据访问的时候,总是想着一定会有人来争抢资源,所以悲观
锁是在用之前就上锁。数据库中的行锁,表锁等,读锁、写锁等都属于悲观锁,另外synchronized锁和Lock锁都是悲观锁机制 -
适用场景
- 悲观锁适用于多写的应用场景,多写的场景发生线程冲突的情况较多,适合适用之前就加锁
-
-
乐观锁
-
对应于现实生活中乐观的人,即把什么事都想得比较好,在进行数据访问的时候,总是认为没有人访问,所以乐观锁是在使用
数据的时候不会上锁,而是在最后更新的时候通过版本号机制或CAS算法判断有没有其他线程更新,再决定是否驳回操作 -
适用场景
- 乐观锁适用于多读应用程序,在这种多读情景下,线程发生冲突较少,适合使用前不加锁
-
版本号机制
- 数据库每张表都有一个版本号,每次更新版本号都会加1,在读数据库的时候,也会读取该表的版本号,当对表中数据做更新
的时候,会检查此刻的版本号跟起初读的版本号是否一致,如果不一致,将驳回本次更新操作
- 数据库每张表都有一个版本号,每次更新版本号都会加1,在读数据库的时候,也会读取该表的版本号,当对表中数据做更新
-
CAS算法
- 无锁算法,所以又叫非阻塞同步
-
线程通信
-
Object类提供了三个控制线程的方法,该方法由线程同步监视器调用
也就是说用synchronized方法使线程同步才可以用Object的三个方法
- wait()
- notify()
- notifyAll()
-
Condition接口下的方法控制线程通信
-
获取Codition的实例
-
API
- await()
- singal()
- singalAll()
-
线程其他相关类
-
ThreadLocal
如果只是纯粹为了隔离共享资源,达到线程同步的效果,那么更适合用ThreadLocal线程局部变量,如果如果多个线程需要共享资源以达到线程通信,则使用线程同步机制
-
代表线程局部变量,在共享对象内部创建一个ThreadLocal类型的待泛型的成员变量即可
-
API
-
set(T value)
- 设置线程副本的值
-
get()
- 获取线程变量副本的值
-
remove()
- 删除此线程局部变量中当前线程的值
-
-