记录自己的JavaSE学习的笔记,学习课程是通过B站尚硅谷的视频学习。
@description JavaSE基础语法及高级特性
@author Van
①基本语法:
数据类型:
1、基本数据类型(primary type):
1、数值型:byte、short-->int-->long-->float-->doble,常量默认int一般整数用int,小数用double
2、字符型:char
3、布尔型:boolean
2、引用数据类型(reference type):
1、类:class
2、接口:interface
3、数组:Array
3、数据类型转换:
自动转换:范围小转范围大,不可以大转小
强制转换:可以范围大转范围小,但是可能会丢失精度
所有变量:
1、成员变量:
实例变量:不以static修饰
类变量:以static修饰
2、局部变量:
形参:方法、构造器中定义
方法局部变量
代码块局部变量
运算符:
算术运算符:+-*/正负号%自加自减字符串连接的加号,自加自减不会改变数据本身的数据类型
幅值运算符:=,扩展:+=、-=、*=、/=、%=
判断运算符:==、!=、<、>、<=、>=、instanceof(检查是否是类的对象,例:"hello" instanceof String)
逻辑运算符:&逻辑与、|逻辑或、!逻辑非、&&短路与、||短路或、^逻辑异或··<尽量使用短路与或>(短路就是判断(&判断为假,|判断为真)后后面的判断(包括加减乘除等)就直接不看了)
位运算符:<<、>>、>>>、&、|、^、~(依次为,左移、右移、无符号右移、与、或、异或、取反)
三元运算符:(条件表达式)?表达式1:表达式2,条件表达式为真执行表达式1,为假执行表达式2 <例: int num = (m>n)?true:false 前面定义m和n的值>
if else结构:
表达式互斥关系,elseif的位置可以随意,无所谓
表达式交集关系,根据实际情况放else if的位置
表达式包含关系,else if条件范围小的在范围大的上面
switch 结构:
case后会继续向下执行,直到遇到break或者把switch·case执行完毕,break是可选项,根据实际情况决定加不加break
switch中的表达式数据类型:byte、short、char、int、枚举类型、String类型
case后的声明只能是常量,不能声明范围
default=if中的最后那个else,default是可选的
在表达式判断不多的情况下可以使用switch-case,又可以使用if else,优先考虑switch-case。原因:效率稍微高一点点(差不多)
while和dowhile:
while(条件){语句},do{语句}while(条件)
break和continue特殊关键字:
两个关键字后都不可以再声明执行语句
break label 和 continue label 结束指定标识的一层循环,可以结束双重循环中的外面那层,label可以放在for循环前,例如label:for(){}
break(结束整个循环):用于swtich和循环结构
continue(结束本次循环):用于循环结构
return:return是结束整个方法,不是用于结束循环结构。
String类型
String.charAt(0),取字符串第一个字符
String.equals("a"),判断字符串是不是“a”,返回boolean
②数组:
概念:
是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一的管理
数组名,下标(索引),元素,数组长度
特点:
有序排列
数组属于引用数据类型,数组元素可以是基本数据类型也可以是引用数据类型
创建数组对象会在内存中开辟一整块连续的空间
数组长度一旦确定不可以再修改
声明和初始化:
一维数组:
静态:int[] ids = new int[]{1,2,3,4};
动态:int[] ids = new int[5];
二维数组:
静态:int[][] ids = new int[][]{{1,2,3,4},{1,2,3}};
动态:int[][] ids = new int[5][3];
int[][] ids = new int[5][];//前面必须写,后面可以不写
数组一旦初始化,那么数组长度就确定了
内存解析(简化结构):
组成:栈(stack)、堆(heap)、方法区(method area)
栈:主要存放局部变量
堆:主要存放new出来的结构,如对象、数组。
方法区:主要有常量池、静态域和一些其他的类加载的东西
浅拷贝深拷贝:
浅拷贝:int[] a,b a = new int[]{1,2} b=a,这个时候就是浅拷贝,b的值在内存中存的是a指向的数组的值,此时栈中有ab两个变量都只指向了堆中的数组,且堆中只有一个数组。
深拷贝:int[] a = new int[]{1,2} int[] b = new int[]; 然后通过for循环让对应的值复制过去,此时栈中有ab两个变量,但是各自指向自己对应的数组,此时堆中有两个数组
查找:
线性查找:用for循环线性遍历查找,效率不高。
二分法:从找中间判断,小的在左边,大的在右边,然后继续找中间,直到中间那个值等于找的值就是找到了,思想类似于快速排序。
工具类的使用(一些常用的数组方法):
boolean equals(int[] a,int[] b):判断两个数组是否相等
String toString(int[] a):输出数组信息
void fill(int[] a,int val):将指定值填充到数组之中
void sort(int[] a):对数组进行排序
int binarySearch(int[] a,int[] key):对排序后的数组进行二分法检索指定的值
数组常见异常:
数组角标越界异常:ArrayIndexOutOfBoundsException
空指针异常:NullPointerException
③面向对象编程(OOP)
理解“万事万物皆对象”:
1、在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体功能。
2、涉及到Java与HTML、数据库交互式,前后端的结构在Java层面交互时,都提现为类、对象
类:
概念:对一类事物的描述,类是抽象的、概念上的定义
成员:
属性:
属性==成员变量==field==域、字段
对象.属性
属性和局部变量的区别:
属性声明在class中,局部变量声明在方法中,有实参和形参两种。
属性可以在声明时,指出其权限,使用权限修饰符,局部变量不可以使用权限修饰符。
属性可以根据类型有默认初始化值:整形(0)、浮点型(0.0)字符型('0'或者'\u0000')、布尔型(false)、引用数据类型(null)
局部变量没有初始化值,调用前必须显示幅值。
对象的属性是放在对象所在的堆范围中(非static),局部变量加载到栈中。
方法(行为):
方法==成员方法==函数(C中)==method
对象.方法()
声明:权限修饰符 返回值类型 方法名(形参){方法体},形参可要可不要,也可以加static、abstract、final等关键字
在返回值类型为void时通常不使用return,如果用只能是"return;"表示结束此方法。有返回值类型必须return对应类型的常量或者变量
可以调用当前类的属性和方法
构造器(构造方法、constructor):
特征:与类同名的方法名、不声明返回值类型、不能被static、final、synchronized、abstract、native修饰、不能有return语句
作用:创建对象、初始化对象属性
构造器可以实现重载,但是一旦显示的定义了构造方法之后无参构造方法需要自己定义出来
this:
可以调用当前类的属性、方法、构造器
指本类对象。通常情况下可以省略“this.”,但是在set的时候不能省略(形参和属性重名)
可以在构造器中使用this(形参列表)调用其他构造器,实现减少代码冗余。不能通过this调用自己
如果一个类中有n个构造器,那么只能用this调用n-1个
this(形参列表)必须声明在当前构造器的首行
代码块(初始化块):
1、特征:"{}",只有一对大括号
2、作用:用来初始化类、对象
3、代码块如果有修饰的话,只能使用static:
>静态代码块:
>内部可以有输出语句
>随着类加载的时候就会执行,且只能执行一次
>作用:初始化类的信息
>静态代码块只能调用静态的属性和静态的方法,不能调用非静态结构
>非静态代码块:
>内部可以有输出语句
>随着创建对象的时候就会执行
>每创建一个对象,就执行一次代码块
>作用:可以在创建对象时,对对象的属性等进行初始化
>非静态代码块可以调用静态的属性和方法和非静态的属性和方法
>代码块在初始化时优先构造器执行
>如果一个类中定义了多个代码块,则按照声明的先后顺序执行(注意静态和非静态)
内部类:
1、Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
2、内部类的分类:
a、成员内部类:在类中,方法等外
>静态成员内部类
>非静态成员内部类
>作为类:(跟正常的类都一样)
> 可以定义属性、方法、构造器等
> 可以用final修饰,可以被abstract修饰
>作为成员:(尽管本身是一个类,但因为作为成员可以跟成员一样)
>调用外部类的结构:外部类的属性或方法
>可以被static修饰
>可以被4中不同的权限修饰
b、局部内部类:方法内、代码块内、构造器内
>规定:在开发中,局部内部类的方法中如果调用局部内部类的方法中的局部变量,局部变量必须声明成final
3:需要关注的3个问题:
a、如何实例化成员内部类的对象
>静态:外部类.内部类 name = new 外部类.内部类();
>非静态:先把外部类实例化p ---> 外部类.内部类 name = p.new 内部类()
b、如何在成员内部类中区分调用外部类的结构
>name(形参)、this.name(内部类的属性)、外部类.this.name(外部类的属性)
c、开发中局部内部类的使用
>在一个方法中需要让一个类实现一个接口然后返回出来一个实现了接口的对象
类可以用来创建数组,例如 Student[] stu = new Student();代表数组Stu里面的数据类型为Student
对象:
概念:实实在在的一个个体,在内存中创建了空间来存放的东西,创建类的对象=类的实例化
创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(static除外),改变一个对象的属性,不会改变其他对象的属性。
匿名对象:
理解:我们创建的对象,没有显示的幅给一个变量名,即为匿名对象
特征:匿名对象只能调用一次
对象内存解析:
内存区域成员:方法区(method area) 虚拟机栈(VM Stack) 堆(heap) 程序技术器
堆:此内存区域的唯一目的就是存放对象实例,几乎所有的实例都在这里分配内存。(当然也包括数组)
栈:指虚拟机栈,用于存贮局部变量等。局部变量表存放了编译器可知长度的各种基本数据类型、
对象引用(应用类型,不同于对象本身,是对象在堆内存放的首地址)方法执行完会自动释放
方法区:
用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
String s1 = “hello”,s1存放在栈中,hello是存放在方法去常量池中。String是一个不可变的字符序列。
引用类型的变量,只可能存储两类值:null和地址值(含变量的类型)
方法:
方法的重载:
概念:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
特点:与返回值类型无关,只看参数列表,且参数列表必须不同。调用时根据参数列表的不同来区别。
例子:Arrays类中的binarySerach()方法可以查找int、String等数组,通过方法传进去的参数(数据类型不同)来区分同名方法
可变个数形参方法:
必须声明在形参的末尾。而且只能声明一个可变个数形参
格式:方法(数据类型 ... 形参名){}(只能传入一种数据类型)
觉得像ifelse中的else,方法传的参数可以为0到任意个,前提是前面没有对应固定个数的形参,有就直接进入对应参数的方法。
可变个数形参方法可以作为类中的重载使用,但是不能和形参为同类型数组同时出现,会报同名方法错误,不能实现重载。
可变个数形参方法等同于方法传入的是一个数组,只是更加简便。
值传递机制:
如果参数是基本数据类型,此时的实参幅给形参的是实参真是存储的数据值
如果参数是引用数据类型,此时的实参幅给形参的是实参存储数据的地址值
完成一个项目(或功能)的思路:
1、根据问题需要,选择问题所针对的现实世界中的实体
2、从实体中寻找解决问题的相关的属性和功能,这些属性和功能就形成了概念世界中的类
3、把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言
把类构造成计算机能够识别和处理的数据结构。
4、将类实例化成计算机世界中的对象,对象是计算机世界中解决问题的最终工具
封装:
概念:
封装性的提现:隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统的可扩展性。
1、设置get、set
2、不对外暴露的私有方法
3、单例模式(将构造器私有化)
4、如果不希望类在包外被调用,可以将类设置为缺省的。
高内聚,低耦合:
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅对外暴露少量的方法用于使用
设置属性私有化(private),通过getset来实现对属性的获取和设置
权限:
由小到大:private、dufault(缺省)、protected、public
可以分别用于修饰类的内部结构:属性、方法、构造器、内部类
修饰类只能用缺省、public
继承:
所有的Java类都直接或间接的继承与Java.lang.Object类,称为:超类
好处:
1、减少代码冗余,提高了代码的复用性
2、便于功能的扩展
3、为之后的多态的使用,提供了前提
格式:class A extends B{}
A:子类、派生类、subclass
B:父类、基类、superclass
体现:
一旦子类继承父类后,子类中就获取了父类中声明的所有的属性和方法
private的属性和方法同样会被继承,只是因为封装让子类不能直接调用,可以通过set、get调用
子类继承父类后可以定义自己特有的属性和方法,实现功能扩展
继承规定:
1、一个类可以被多个子类继承
2、Java中的单继承性:一个类只能有一个父类
3、子父类是相对的概念
4、子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5、子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
子类对象实例化的全过程:
1、从结果上看:(继承性)
>子类继承父类后,就获取了父类中声明的属性和方法
>创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
2、从过程上看:
>当我么通过子类的构造器创建子类对象时,我们一定会直接或间接的调用父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。
正因为加载过所有父类的结构,所以才可以看到内存中有父类的结构,子类对象才可以考虑进行调用
>明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
重写:
1、子类继承父类之后,可以对父类中同名同参的方法进行覆盖操作
2、应用:
重写以后,创建子类对象来调用父类中的同名同参方法时,实际执行子类覆盖的方法
父类对象调用就是调用自己的方法
3、重写的规则:
约定俗称:子类中的叫重写方法,父类中的叫被重写方法
1、重写方法的方法名和形参列表和被重写方法相同
2、重写方法的权限修饰符 >= 被重写方法的权限修饰符
>特殊情况:子类不能重写父类中private的方法
3、返回值类型:
>父类被重写方法的返回值是void,则子类重写的方法也只能是void
>父类被重写方法的返回值是A类型,则重写方法的返回值可以是A类或A类的子类
>父类被重写方法的返回值是基本数据类型,则重写方法必须是相同的基本数据类型
4、子类重写的方法抛出的异常类型 <= 父类被重写的方法抛出的异常类型
4、子类父类中的同名同参方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)
5、要知道重载和重写的区别
>重载重写的概念
>重载和重写的具体规则
>从编译和运行的角度看:
>重载:是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。
对编译器来说,同名方法=不同的方法。这些方法的调用地址在编译器就确定了。
所以对重载而言,在方法调用之前,就已经确定了所要调用的方法,称为"早绑定"或"静态绑定"
>多态:只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,称为"晚绑定"或"动态绑定"
如果不是"晚绑定",那么就不是多态
super关键字:
1、super可以用来调用父类的属性、方法、构造器
2、在子类的构造器或者方法中,通过使用"super.属性"或"super.方法"的方式显示调用父类中的属性和方法。
>通常情况:我们习惯省略"super"
>特殊情况:当子类和父类中定义同名的属性时,要在子类中调用父类的同名属性,就需要显示的使用"super.属性"来调用
>特殊情况:当子类重写了父类的方法,要调用父类被重写的方法要用"super.方法"来调用父类被重写方法
3、super调用构造器:
>在子类的构造器中显示的使用"super(形参列表)"的方式调用父类指定的构造器
>"super(形参列表)"的使用,跟this一样必须声明在构造器的首行
>在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
>在构造器的首行,没有显示的声明"this(形参列表)"或"super(形参列表)",则默认调用父类的空参构造器
>在类的多个构造器中,至少有一个构造器中使用了"super(形参列表)"调用父类的构造器
多态:
理解(概念):可以理解为一个事物的多种形态
实现代码的通用性,例如Object类中的public boolean equals(Object obj){}
JDBC:使用java程序操作数据库()放的也是父类,可以根据具体数据库使用子类实现多态
抽象类、接口的使用肯定提现了多态性。(抽象类和接口不能实例化)
1、对象的多态性:父类的引用指向子类的对象(Person p = new Man())。
2、多态的使用:
>当调用子父类同名同参方法时,实际执行的是子类重写的方法--->虚拟方法调用。
>在多态情况下,子类重写了父类方法,此时父类方法称为虚拟方法,父类根据幅给它的不同的子类对象,动态调用子类方法。编译器无法识别是调用哪个方法
>虚拟方法调用:
>在编译期,只能调用父类中声明的方法,但在实际运行期,我们实际执行的是子类重写父类的方法
>总结:编译,看左边;运行,看右边。得出多态是一个运行时行为。
>只适用于方法,不适用于属性
3、多态的使用前提:
>类的继承关系
>方法的重写
4、对象有了多态之后,内存中实际是加载了子类特有的属性和方法,但由于声明是父类,导致无法调用子类特有属性和方法
>使用强制类型转换符(向下转型):Man man = (Man)p;
>不相关的两个对象是不可以幅值的
>使用强转时,可能出现ClassCastException异常
instanceof关键字:
>a instanceof Array:判断对象a是否是A的实例。如果是返回true,不是返回false
>使用场景:为了避免在向下转型时出现ClassCastException异常,在转型之前先判断,再决定是否向下转型
抽象类和抽象方法:
>关键字:abstract,可以用来修饰的结构:类、方法
1、抽象类:
>概念:随着继承层次中子类的定义,类越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。
有事将一个父类设计的非常抽象,以至于它没有具体的实例,这样的类叫抽象类
>如果类被abstract修饰之后就不能再实例化了
>抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
>开发中,都会提供抽象类的子类,让子类对象实例化,完成相应操作
2、抽象方法:
>抽象方法只有方法的声明,没有方法体
>包含抽象方法的类,一定是抽象类;反之抽象类可以没有抽象方法
>若子类重写了父类中的所有的抽象发放后,此子类方可实例化
若没有重写所有抽象方法,则子类也是一个抽象类
3、注意点:
>abstract不能用来修饰:属性、构造器等结构
>abstract不能用来修饰私有方法、静态方法、final
4、抽象类的匿名子类:
Person p = new Person(){//重写前面的抽象方法} --->Person是一个抽象类 右边等于一个Person的匿名子类创建了一个对象
接口(interface):
1、概念:
a、因为Java的单继承性,有时需要从几个类中派生一个子类。所以有了接口就可以实现多重继承的效果
b、接口就是规范,定义的是一组规则。继承是一个“是不是”的关系,而接口实现则是“能不能”关系。(继承:学生是人的子类,那么学生是人,”是不是“的关系)
c、接口的本质是契约、标准、规范
2、Java中,接口和类是并列的两个结构
3、如何定义接口;定义接口中的成员
a、JDK7以前:只能定义全局常量和抽象方法
>全局常量:public static final的(可以省略不写)
>抽象方法:public abstract的
b、JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
>静态方法:public static void的
>接口中定义的静态方法,只能通过接口来调用
>默认方法:public default void的
>通过实现类的对象,可以调用接口中的默认方法
如果实现类重写了接口中的默认方法,调用时,然后调用的是重写的方法
>如果子类(或实现类)继承的父类和实现的接口中声明了同名同参的方法,那么子类在没有重写的情况下,默认调用父类的方法--->类优先原则
>如果实现类实现了多个接口,而这多个接口中定义了同名同参的默认方法,那么在实现类没有重写的情况下,报错 --->接口冲突;就必须要需要重写此方法
>在实现类重写了接口的默认方法之后,可以通过"接口名.super.方法名"的方式调用接口的默认方法
4、接口中不能定义构造器!意味着接口不能实例化
5、Java开发中,让类实现接口的方式来使用
>如果实现类实现了接口中的所有抽象方法,则此实现类就可以实例化
>如果实现类没有实现接口中的所有抽象方法,则此实现类仍为一个抽象类
6、Java类可以实现多个接口 --->弥补了Java单继承性的局限性
>格式: class B implements AA,BB,CC,DD{}
7、接口匿名实现类:
和抽象类实现完全一样;Person p = new Person(){//实现接口的抽象方法} --->Person是一个接口
8、接口与接口之间可以继承,而且可以多继承
9、接口的使用,能够提现多态性
10、接口,实际上可以看做是一种规范
11、接口的应用(了解就行):
a、代理模式:
>是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问
b、工厂模式:
>达到提高灵活性的目的
>抽象类和接口有哪些异同?
相同点:
>都不能实例化
>都可以被继承
>一般都有抽象方法
不同点:
>抽象类:
>有构造器
>只能单继承
>接口:
>没有构造器
>可以多继承
Object类:
1、java.lang.Object是所有Java类的父类,称为超类。
2、如果在类声明中为使用extends指明父类,则默认继承object类
3、Object类中的功能具有通用性:
重点:
>equals():比较两个对象是否相等
1、==和equals()区别:
> == :运算符:
a、可以使用再基本数据类型变量和引用数据类型变量中
b、如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(类型不一定要相同)
如果比较的是引用数据类型变量:比较两个变量地址是否相同;即两个引用是否指向同一个实体
2、equals():方法:
>只能适用于引用数据类型
>object类中的equals()和==是相同的
>String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后比较的不是引用的地址是否相同,而是比较两个对象的"实体内容"是否相同
>通常情况下,我们自定义的类使用equals()方法,也是比较两个对象的”实体内容“是否相同,就需要对Object类中的equals()进行重写
>toString():
1、当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
2、String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()方法时,返回”实体内容“信息
3、自定义类也可以重写toString()方法,当调用此方法时,返回”实体内容“
了解:
>clone():复制一个对象
>finalize():垃圾回收的一个方法,尽量不要自己调用,系统会自动调用
final、finally、finalize的区别:
final:关键字,表用这个数据不会再变,成为一个常量
finally:trycatch中的一个关键字
包装类(Wrapper):
1、Java提供了8中基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
2、基本数据类型、包装类、String三者之间的转换:
>基本数据类型---->包装类:
>调用包装类的构造器:boolean 在创建对象使用构造器时可以随便放入指,不一样的会返回false
>包装类---->基本数据类型:
>调用包装类xxx的xxxValue();“xxx”是基本数据类型
>基本数据类型、包装类---->String类型:
>String str = num1 + "";(num为其他数据类型)
>调用String类的valueOf()方法
>String str1 = String.valueOf(num1);(num1可以是基本数据类型和包装类)
>String类型---->基本数据类型、包装类:
>调用包装类的parseXxx(String s)方法
>int num1 = Integer.parseInt(str1);
>注意:除了boolean都有可能报NumberFormatException异常
3、JDK 5.0 新特性:自动装箱 and 自动拆箱:
>解决了前面基本数据类型和包装类相互转换的过程,不需要再手动转换,直接自动转换
4、Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127返回的整数。
如果我们使用自动装箱的方式,给Integer的赋值的范围在-128~127之间,可以直接使用数组中的元素,不用再去new,地址就是相同的
单元测试:
1、选中当前工程-右键选择:build path -add libraries -JUnit 4 - 下一步
2、创建Java类进行单元测试:
Java类要求:
>此类是public的
>此类提供公共的无参构造器
3、此类中声明单元测试方法:
此时的单元测试方法,方法权限是public,没有返回值,没有形参
4、此单元测试方法上需要声明一个注解@Test并import
5、声明好单元测试方法以后,就可以在方法体内测试相关代码
6、写完代码以后,左键双击单元测试方法名,右键:run as JUnit Test
说明:
>如果执行结果没有任何异常:绿条
>如果执行结果出现异常:红条
static关键字:
1、可以用来修饰:属性、方法、代码块、内部类
>static修饰属性:
>属性:按是否使用static修饰,分为:静态属性 vs 非静态属性(实例变量)
实例变量:创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,修改一个对象的属性不会改变其他属性的值
静态属性(静态变量):每个对象都共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用时也是被修改过的
>static修饰属性其他说明:
a、静态变量随着类的加载而加载
b、静态变量要早于对象的创建
c、由于类只加载一次,则静态属性在内存中也只会加载一次(存在一份);存放在方法区的静态域中
d、 类变量 实例变量
类 yes no
对象 yes yes
>static修饰方法:
>随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
> 静态方法 非静态方法
类 yes no
对象 yes yes
>静态方法中,只能调用静态方法或属性
非静态方法中,既可以调用非静态的方法和属性,也可以调用静态方法和属性
>static注意点:
>在静态方法中,不能使用this和super关键字
>关于静态属性和静态方法的使用,从声明周期的角度去理解
2、开发中,如何确定一个属性或方法要声明为static:
>属性:属性是可以被多个对象所共享的,不回随着对象的不同而不同的
类中的常量,也常常被声明成static
>方法:操作静态属性的方法,通常设置为static
工具类中的方法,习惯上声明为static,比如Math、Arrays、Connection等
final关键字:
1、可以用来修饰的结构:类、方法、变量:
>修饰类:
>final class Van{} 代表不能再被继承,没有子类;
>比如String类、System类、StringBuffer类
>修饰方法:
>final van(){} 表明此方法不可以再重写;
>比如Object类中的getClass()
>修饰变量:
>被final修饰的"变量"就称为是一个常量
>修饰属性:
>修饰属性一般属性就是全大写,代表是final修饰的属性-常量
>可以考虑赋值的位置有:显示初始化、代码块初始化、构造器初始化
>修饰局部变量:
>使用final修饰形参时,表明此形参是一个常量。调用此方法时,给常量形参赋一个实参,一旦赋值以后,在方法内部就不能再重新赋值
2、static fianl 同时出现:
>修饰属性:称为全局常量
>修饰方法:类加载时被调用且不能再重写
package关键字:
1、为了更好的实现项目中类的管理,提供包的管理
2、使用package声明类或接口所属的包,声明在源文件首行
3、包,属于标识符,遵循标识符的命名规则,“见名知意”
4、每“.”一次,就代表一层文件目录
5、同一个包下不能命名同名的接口和类,不同的包可以
import关键字:
1、在源文件中显示的使用import接口导入指定包下的类、接口
2、java.lang包下的可以省略import,自动导入
3、如果在源文件中,使用了不同包下的同名类,则必须至少有一个类需要以全类名的方式
4、使用“xxx.*”方式可以使用包下所有结构。但是如果在包内的子包就必须要单独import
5、import static:导入指定类或接口中的静态结构,落脚点是类中的结构(属性或方法)import不是。
单例设计模式:
1、概念:采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
首先将类的构造器访问权限设置为private,使外部无法new对象。也因为外部无法得到对象,只能调用该类的某个静态方法以返回类内部创建的对象。
静态方法只能访问类中的静态成员变量,所以指向类内部产生的该类对象的变量也必须定义成静态的
2、使用:
>饿汉式单例模式:
1、私有化类的构造器
2、内部创建类的对象(要求对象也是static)(可以理解为这个对象是这个类的属性) private static Van van = new Van();
3、提供公共的静态方法,返回类的对象
>懒汉式单例模式:
1、私有化类的构造器
2、声明当前类的对象,但是没有初始化 private Van van = null;
3、声明公共静态方法返回当前类的对象,判断van==null,如果空 van= new Van()在返回van
4、解决线程不安全方法:get这个静态方法加synchronized关键字(变成同步方法)
3、区分饿汉式和懒汉式:
饿汉式:好处:是天然线程安全的 坏处:对象加载时间过长
懒汉式:好处:延迟对象的创建 坏处:目前是线程不安全
优点:只生成一个实例,减少了系统性能开销。当一个对象产生需要较多的资源时,则可以通过单例模式产生对象,然后永久驻留内存的方式来解决
④异常:
1、异常体系结构:
java.lang.Throwable:
>-----java.lang.Error:一般不编写针对性的代码进行处理
>-----java.lang.Exception:可以进行异常的处理
>编译时异常(Checked)
>-----IOException
>-----FileNotFoundException
>-----ClassNotFoundException
>运行时异常(Unchecked)
>-----NullPointerException
>-----ArrayIndexOutOfBoundsException
>-----ClassCastException
>-----NumberFormatException
>-----InputMismatchException
>-----ArithmaticException
2、异常处理方式:
>异常处理:抓抛模型:
>过程一:“抛”,程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,抛出后其后的代码就不再执行
>过程二:“抓”,可以理解为异常的处理方式
a、try-catch-finally处理异常
b、throws
1、try-catch-finally处理异常:
>try{//可能出现异常的代码}catch(异常类型1 变量名1){//处理异常的方式1}...finally{//一定会执行的代码}
>catch可以有多个,finally是可选的
>try将可能出现的异常包装起来,一旦出现异常就会生成异常对象,
然后在catch中去匹配,匹配到了某个catch就会进入进行异常处理,处理完成跳出try-catch结构,继续执行后面的代码
>catch中的异常类型如果有子父类关系,子类一定在父类上面,否则会报错
>常用的异常对象处理方式:
>----String getMessage()
>----void printStackTrace()
>在try中定义的变量在出了之后try之后无法使用
>由于JVM无法回收如Scanner、File等,所以一般在finally中关闭,并且关闭也有可能会出异常,所有在try-catch中也可以嵌套try-catch
>体会:
>---使用try-catch-finally处理编译时异常,使得程序在编译时不会再报错,但是运行时仍可能报错
>---开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了,针对编译时异常,我们一定要考虑异常处理
2、throws + 异常类型:
>写在方法的声明处,指明此方法执行,可能会抛出的异常类型。一旦当方法体执行时出现异常,仍会在异常代码出生成一个异常类的对象,此对象满足throws对象时就会抛出
>子类抛出的异常必须比父类小
3、如何选择try-catch还是throws:
a、如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中异常,必须使用try-catch处理
b、执行的方法中,先后又调用了另外的几个方法,这几个方法是递进的关系执行。建议使用throws的方式进行处理,而执行的方法a可以考虑使用try-catch
3、关于异常对象的产生:
1、系统自动生成的异常对象:
2、手动生成一个异常对象,并抛出 throw关键字
4、自定义异常类:
1、建一个类继承RuntimeException或者Exception
2、提供全局常量serivalVersionUID(序列号)
3、提供重载的构造器
⑤多线程:
1、概念:程序、进程、线程
>程序:为完成某个特定的任务,人为编写的一段静态代码
>进程:是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
>线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器,线程的切换开销小
一个进程中可以有多个线程同时运行,他们共享内存单元、内存地址空间等,使得线程间通信更高效、便捷,但是多个线程操作共享资源可能带来安全的隐患
>并行与并发:
>并行:多个CPU同时执行多个任务。例如:多个人同时做多个事
>并发:一个CPU(采用时间片)"同时"执行多个任务。例如:秒杀、多个人做同一件事
2、多线程的优点:
>提高应用程序的相应。对图形化界面更哟更有意义,可增强用户体验
>提高计算机系统CPU的利用率
>改善程序结构。将即长又复杂的进程分为多个线程,独立运行,利用理解和修改
3、何时需要多线程:
>程序需要同时执行两个或多个任务
>程序需要实现一些需要等待的任务时,入用户输入、文件读写操作、网络操作、搜索等
>需要一些后台运行的程序时
4、多线程的创建:
--->JDK5.0新增线程创建方式
>新增方式一:实现Callable接口:
>与runnable接口相比,Callable功能更加强大
>相比run()方法,可以有返回值
>方法可以抛异常
>支持泛型
>需要接触FutureTask类,比如获取返回结果
Future接口:
>可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等
>FutureTask是Future接口的唯一的实现类
Callable接口的使用:
1、创建一个实现Callable的实现类
2、实现call(),将此线程需要执行的操作声明在call()中
3、创建Callable接口的实现类对象
4、将此Callable接口的实现类对象传递到FutureTask构造器中,创建FutureTask的对象
5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
6、如果想看call的返回值,可以调用FutureTask.get()获取返回值
>新增方式二:使用线程池
>思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
>好处:
>提高相应速度(减少了创建新线程的时间)
>降低资源消耗(重复利用线程池中线程,不需要每次都创建
>便宜线程管理
属性:
>courPoolSize:核心池的大小
>maximumPoolSize:最大线程数
>keepAliveTime:线程没有任务时最多保持多长时间后会终止
>JKD5.0提供了线程池相关API:ExecutorService和Executors
>ExecutorService:
>真正的线程池接口,常见子类ThreadPoolExecutor
>void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
><T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
>void shutdown():关闭连接池
>Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
>Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池Executors.newFixedThreadPool(n);创建一个可重用固定线程数的线程池
>Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
>Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
使用:
1、提供指定线程数量的线程池
2、执行指定的线程操作,需要提供实现Runnable接口或Callable接口实现类的对象
3、关闭连接池
1、方式一:继承于Thread类:
>创建一个继承于Thread类的子类
>重写Thread类的run() --->不能直接调用run(),只能调start()去创建线程调用
>创建Thread类的子类的对象 --->一个对象不能调两次start(),不能让已经创建的线程再去创建执行
>通过此对象调用start()--->启动当前线程;再在当前线程调用run()
a、Thread类的常用方法:
--->start():启动当前线程;调用当前线程的run()
--->run():通常需要重写Thread类中的run(),将创建的线程要执行的操作声明在此方法中
--->currentThread():静态方法,返回执行当前代码的线程
--->getName():获取当前线程的名字
--->setName():设置当前线程的名字
--->yield():释放当前CPU的执行权(有可能释放后又安排到当前线程)
--->join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完后,线程a才结束阻塞状态
--->stop():强制结束当前线程(已过时,不建议使用)
--->sleep(long millitime):让当前线程睡眠millitime毫秒,睡眠时是阻塞态
--->isAlive():判断当前线程是否存活
2、方式二:实现Runnable接口:
>创建一个实现了Runnable接口的类
>实现类去实现runnable中的抽象方法:run()
>创建实现类的对象
>将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
>通过Thread类的对象调用start()方法
3、比较两种线程创建的方式:
>开发中:优先选择:实现runnable接口的方式
>原因:
--->实现的方式没有类的继承性的局限性
--->实现的方式更适合来处理多个线程有共享数据的情况
>相同点(联系):两种方式都需要去实现runnable接口(Thread类已经写好了实现runnable接口)
4、线程的调度:
>调度策略:时间片、抢占式(高优先级的线程抢占CPU)
>Java的调度方法:
>同优先级线程组成先进先出队列(先到先服务),使用时间片策略
>对高优先级,使用优先级调度的抢占式策略
>线程的优先级:
>MAX_PRIORITY:10
>MIN_PRIORITY:1
>NORM_PRIORITY:5
>获取和设置线程的优先级
--->getPriority():获取线程的优先级
--->setPriority(int p):设置线程的优先级
>说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行,
并不意味着只有当高优先级的线程执行完后才执行低优先级
5、线程的声明周期:
线程的五种状态:
>新建:当一个Thread类或其子类被创建对象时,新生的线程对象处于新建状态
>就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时他已经具备了运行的条件,但是还没有分配到CPU资源
>运行:当就绪态的线程被CPU调用时,变进入运行状态,run()方法定义了线程的操作和功能
>阻塞:在某种特殊情况下,被认为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行
>死亡:线程完成全部工作或线程被提前强制性的中止或出现异常导致结束
6、线程的同步(解决线程安全):
>方式一:同步代码块
>关键字:synchronized
synchronized(同步监视器){//需要被同步的代码} //不能包含代码多了也不能少了
>说明:操作共享数据的代码,即为需要被同步的代码
共享数据:多个线程共同操作的变量(数据),需要使用同步机制讲操作共享数据的代码包起来
>同步监视器:俗称--->锁。任何一个类的对象,都可以充当锁
>要求:多个线程必须要公用同一把锁。--->同一个对象
>补充:
a、在实现Runnable接口创建多线程的方式中,我们可以考虑使用this(看是否是一个对象)
b、在继承Thread类创建多线程的方式中,慎用this,可以用类名.class代表对象
>同步的方式:
>好处:解决了线程的安全问题
>坏处:操作同步代码时,只能有一个线程参与,其他线程等待。相当于时一个单线程的过程,效率低----->局限性
>方式二:同步方法
>关键字:synchronized,在方法处加synchronized关键字
>runnable接口使用同步方法:
>把同步代码块的代码放入一个声明了synchronized的方法中
>此时的方法的同步监视器是this
>Thread类使用同步方法:
>把同步代码块的代码放入一个声明了synchronized的方法中,还要加一个static关键字,此时同步监视器就是类名.class
>此时的方法的同步监视器是this
总结:
>同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
>非静态同步方法,同步监视器是:this
>静态同步方法,同步监视器是:类本身
>方式三:Lock(锁)JDK5.0新增
>在实现类中创建一个ReentrantLock对象lock
>将需要处理出现线程安全的代码放入try中,在try首部位置调用 lock.lock()---》等于添加了同步监视器
>在finnal里加一个lock.unlock()---》取消锁
面试题:synchronized和Lock的区别:
>相同点:
>不同点:
>Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
>synchronized机制在执行完相应的同步代码后,自动的释放同步监视器
面试题:如何解决线程安全问题?有几种方式:
>3种:Lock、同步代码块、同步方法
>使用优先顺序:
Lock>同步代码块>同步方法
7、死锁问题:
>概念:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
>说明:出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
死锁产生的4个必要条件
1、互斥: 某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待: 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3、不可抢占: 别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4、循环等待: 存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
避免死锁的方法
1、死锁预防 ----- 确保系统永远不会进入死锁状态
产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。
a、破坏“占有且等待”条件
方法1:所有的进程在开始运行之前,必须一次性地申请其在整个运行过程中所需要的全部资源。
优点:简单易实施且安全。
缺点:因为某项资源不满足,进程无法启动,而其他已经满足了的资源也不会得到利用,严重降低了资源的利用率,造成资源浪费。
使进程经常发生饥饿现象。
方法2:该方法是对第一种方法的改进,允许进程只获得运行初期需要的资源,便开始运行,在运行过程中逐步释放掉分配到的已经使用完毕的资源,然后再去请求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。
b、破坏“不可抢占”条件
当一个已经持有了一些资源的进程在提出新的资源请求没有得到满足时,它必须释放已经保持的所有资源,待以后需要使用的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。
该种方法实现起来比较复杂,且代价也比较大。释放已经保持的资源很有可能会导致进程之前的工作实效等,反复的申请和释放资源会导致进程的执行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。
c、破坏“循环等待”条件
可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程占有编号为i的资源时,那么它下一次申请资源只能申请编号大于i的资源。
>解决方法:
>专门的算法、原则
>尽量减少同步资源的定义
>尽量规避嵌套同步
8、线程的通信:
>涉及到的三个方法:
>wait():一旦执行此方法,当前线程
>notify():唤醒被wait()的一个线程,如果有多个线程,优先唤醒优先级高的,如果优先级一样随机唤醒
>notifyAll():唤醒所有被wait()的线程
--->说明:
a、三个方法必须使用在同步代码块或者同步方法中
b、三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现IllegalMonitorStateException异常
c、三个方法都是定义在java.lang.Object类中的
面试题:sleep()和wait()的异同:
相同点:都可以使得当前的线程进入阻塞状态
不同点:
1、两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
2、调用的要求不同:sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或者同步方法中
3、关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放监视器,wait()会释放
⑥常用类:
1、字符串相关的类:
1、String类:
>String类是一个final类,代表不可变的字符序列,不可被继承
>字符串是常量,用双引号表示,他们的值在创建之后不能更改
>String对象的字符内容是存储在一个字符数组value[]中的
>String实现了Serializable接口:表示字符串是支持序列化的
实现了Comparable接口:表示可以比较小大
>使用String.replace()方法修改字符串时,也需要重新指定内存区域造一个新的字符串
>在字面量定义时加final关键字,变量拼接时也会在常量池
>JDK9.0之后底层数组由char[]变成了byte[]
a、String对象的创建方式:
>String str = "van"; //这是字面量定义的方式
--->此时的str是生命在方法去的字符串常量池中
>String str = new String();//还有很多构造器可以传入
--->此时的str是数据在堆空间中开辟空间以后对应的地址值
--->String str = new String("abc"),一共创建了2个对象,一个是new的堆空间的对象,一个是"abc"在常量池中的数据
b、常用方法:
>intern():调用后得到的数据返回的地址一定是常量池中的地址(new的String变量的地址是堆中不是在常量池)
>int length():返回字符串长度
>char charAt(int index):返回某索引处的字符
>boolean isEmpty():判断是否是空字符串
>String toLowerCase():使用默认语言环境,将String中的所有字符转换成小写
>String toUpperCase():使用默认语言环境,将String中的所有字符转换成大写
>String trim():返回字符串的副本,忽略前导空白和尾部空白
>boolean equals(Object obj):比较字符串内容是否相同
>boolean equalsIgnoreCase(String anotherString):比较字符串内容是否相同,但是忽略大小写
>String concat(String str):将指定字符串连接到此字符串的结尾 等价于"+"
>int compareTo(String anotherString):比较两个字符串的大小
>String substring(int beginIndex):返回一个新的字符串,它是此子串串从beginIndex开始截取的
>String substring(int beginIndex,int endIndex)(左闭右开):返回一个新的字符串,它是此子串串从beginIndex开始截取到endIndex结束
>boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
>boolean startsWith(String prfix):测试此字符串是否以指定的前缀开始
>boolean startsWith(String prfix,int toffset):测试此字符串从指定索引开始的子字符串是否以指定开始
>boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true
>int indexOf(String str):返回指定子字符串在此字符串中第一次出现的索引
>int indexOf(String str,int fromIndex):返回指定子字符串在此字符串中第一次出现的索引,从指定的索引开始
>int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现的索引
>int lastIndexOf(String str,int fromIndex):返回指定子字符串在此字符串中最右边出现的索引,从最后开始反向搜索
>String replace(char oldChar,char newChar):返回一个新的字符串,弄妥newChar替换字符串中所有的oldChar
>String replace(CharSequence target,CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串
>String replaceAll(String regex,String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串
>String replaceFirst(String regex,String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的第一个子字符串
>boolean mathes(String regex):告知此字符串是否匹配给定的正则表达式
>String[] split(String regex):根据给定正则表达式的匹配拆分此字符串
>String[] split(String regex,int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
了解就行
c、String和char[]转换:
>String--->char[]:Char char1 = String.tuCharArray()
>char[]--->String:new String(arr) arr为char[]
d、String和byte[]转换:
>String--->byte[](编码):byte[] bytes = 调用String.getBytes()//转换出来的值为默认编码集
>byte[]--->String(解码):调用String的构造器
2、StringBuffer类:
a、StringBuffer:是一个可变序列,JDK5.0新增的
b、线程安全的,所以效率偏低,几乎所有方法都有synchronized关键字
c、只有多线程有共享数据的才考虑使用StringBuffer
d、三个底层都是用char[]数组存储,JDK9.0之后使用了byte[]存储
StringBuffer可变是因为底层的char[]长度为初始赋值长度+16个字符
e、常见问题:
>StringBuffer.length()得出的是有效长度
>当加的长度超过16涉及到扩容,不需要自己扩容,扩容长度为当前长度*2+2(等于新建了一个StringBuffer),然后把原有复制到新的里面
指导意义:开发中尽量少使用扩容,会影响效率,尽量使用StringBuffer(int capacity)或者StringBuilder(int capacity)指定容量的字符串
f、StringBuufer常用方法:
>StringBuffer的常用方法:
>StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接stringBuffer delete(int start,int end):副除指定位置的内容
>stringBuffer replace(int start, int end,String str):把[start,end)位置替换为strstringBuffer insert(int offset, xxx):在指定位置插入xxx
>stringBuffer reverse() :把当前字符序列逆转
>public int indexof (String str)
>public string substring(int start,int end)public int length()
>public char charAt(int n )
>public void setCharAt(int n ,char chl
主要是标记了意思的方法
3、StringBuilder类:
a、StringBuilder:是一个可变序列,JDK5
b、线程不安全的,所以效率高,没有synchronized关键字
c、单线程尽量使用StringBuilder
4、String、StringBuffer、StringBuilder三个的效率:
>从高到低:StringBuilder>StringBuffer>String
2、关于时间的API:
>JDK8.0之前:
1、System类():
>(称为时间戳)System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日O时0分0秒之间以毫秒为单位的时间差。此方法适于计算时间差。
2、Date类:
>java.util.Date类:
>构造器的使用:
》Date():创建一个当前时间的Date对象
》Date(long date):创建指定毫秒数的Date对象
>两个方法的使用:
》toString():显示当前的年、月、日、时、分、秒
》getTime():获取当前Date对象的时间戳(毫秒数)
>java.util.Date的子类java.sql.Date:
>在使用SQL时才使用,一般使用普通的Date
>创建对象一般只有new Date(long date);使用toString()输出只输出年月日
>util.Date对象--->sql.Date对象:
java.sql.Date date = new java.sql.Date(java.util.date.getTime())
3、java.text.SimpleDateFormat类:
>是对日期Date类的格式化和解析,只能操作Date不能操作Calendar
>两个操作:
>格式化:日期--->字符串
>创建SimpleDateFormat类的实例化对象
>对象.format(Date对象)返回一个格式化后的字符串
>解析:格式化的逆过程:字符串--->日期
>要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器体现,否则跑异常)
>创建SimpleDateFormat类的实例化对象
>对象.parse(Date对象),需要抛异常
>可以通过构造器控制时间的格式来转换
4、java.util.Calendar日历类---抽象类:
>实例化:
>方式一:Calendar是抽象类,需要用子类GregorianCalendar的对象
>方式二:调用Calendar的静态方法getInstance()--->Calendar calendar = Calendar.getInstance()(实际上子类还是子类)
>常用方法:
>get():获取属性,例如:calendar.get(Calendar.DAY_OF_MONTH)
>set():可以将指定的属性设置成指定的值
>add():在原有的基础上添加
>getTime():相当于日历类---> Date
>setTime():Date ---> 日历类
>注意:获取月份时:一月是0,以此类推;获取星期时,周日是1,周六是7
>JDK8.0之后:
1、引入了java.time API(大部分包都不会怎么用到)
>java.time -包含值对象的基础包
>java.time.chrono -提供对不同的日历系统的访问
>java.time.format -格式化和解析时间和日期
>java.time.temporal -包括底层框架和扩展特性
>java.time.zone -包含时区支持的类
2、LocalDate、LocalTime、LocalDateTime
>创建对象:
>LocalDateTime ld = LocalDateTime.now()
>LocalDateTime.of(2020,10,6,13,15,13)
--->注意:Date创建有偏移量,这个没有偏移量
>获取属性:
>getDayOfMonth()
>getDayOfWeek()
>getMonth())
>getMonthvalue()
>getMinute()
>设置属性:
>withDayOfMonth()等
>有不可变性,本身没有变,变的是返回的对象
>添加:
>plusDays()等
>任然有不可变性
>减:
>minusDays()等
>任然有不可变性
3、Instant瞬时:可能被用来记录时间戳
>创建对象:
>Instant instant = Instant.now()按照中时区计算
>方法:
>atOffset(ZoneOffset offset):设置偏移量
>toEpochMilli():返回毫秒数,时间戳
>static ofEpochMilli(long epochMilli):返回毫秒数+指定毫秒数之后的Instant类对象,实例化的一种方式
4、java.time.format.DateTimeFormatter类:用于格式化或解析时间、日期,提供了三种格式化方法
>预定义标准格式
>本地化相关的格式,ofLocalizedDateTime(FormatStyle.LONG)
>自定义格式:ofPattern("yyyy-mm-dd hh:mm:ss E")
解析:parse()
--->自定义比较常用,需要去查询就可
3、比较器:
>两个接口:
>在开发中,需要对对象进行排序
1、自然排序:java.lang.Comparable
>重写compareTo(obj):大于返回正整数,小于返回负整数,等于返回0
>对于自定义类,如果需要排序,可以让自定义类实现Comparable接口重写compareTo()
2、定制排序:java.util.Comparator
>当元素的类型没有实现Comparable接口而又不方便修改代码或排序规则不适合时,可以使用Comparator
>重写conpare(Object o1,Object o2):o1大于o2返回正整数以此类推
4、System类:
>成员变量:System类内部包含in、out和lerr三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
>成员方法:
>native long currentTimeMillis():该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。
>void exit(int status):该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。
>void gc():该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中玩圾回收算法的实现以及系统执行时的情况。
>String getProperty(String key):该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示:
5、Math类:
>常用方法:
abs绝对值
acos,asin,atan,cos,sin,tan 三角函数
sqrt 平方根
pow(doubie a,doble b) a的b次幂iog自然对数
exp e为底指数
max(double a,double b)
min(double a,double b)
random() 返回0.0到1.0的随机数
long round(double a) double型数据a转换为long型(四舍五入)
toDegrees(double angrad) 弧度—>角度
toRadians(double angdeg) 角度—>如度
6、BigInteger和BigDecimal类:(了解)
>BigInteger:表示不可变的任意精度的整数,在int类型满足不了长度时考虑
>BigDecimal:double类型,在商业计算中,要求数字精度比较高
>构造器:
>public BigDecimal(double val)
>public BigDecimal(String val)
>常用方法:
public BigDecimal add(BigDecimal augend)
public BigDecimal subtract(BigDecimal subtrahend)
public BigDecimal multiply(BigDecimal multiplicand)
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
⑦枚举类:
1、枚举类的使用:
>概念:类的对象只有有限个,确定的
>当需要定义一组常量时,强烈建议使用枚举类
>如果枚举类中只有一个对象,则可以作为单例模式的实现方式
2、如何定义枚举类
>方式一:JDK5.0之前,自定义枚举类
1、声明Season对象的属性 private final的属性
2、私有化构造器 private的构造器
3、提供当前枚举类的多个对象 public static final的对象 名称为大写
>方式二:JDK5.0之后,可以使用enum关键字定义枚举类
>使用eunim关键字定义的枚举类默认继承于java.lang.Enum类
1、提供当前枚举类的对象,多个对象之间用",",末尾用";"结束
例如:SPRING("春天","春暖花开"),
2、声明Season对象的属性 private final的属性
3、私有化构造器 private的构造器
3、Enum类的主要方法:
>values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
>valueOf(String str):可以把一- 个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
返回枚举类中对象名是objName的对象
>toString():返回当前枚举类对象常量的名称
4、使用enum关键字定义的枚举类实现接口的情况:
>情况一:实现接口,再enum类中实现抽象方法
>情况二:让枚举类的对象分别实现接口中的抽象方法
⑧注解(Annotation)JavaEE用的比较多:
1、概念:
1、从JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是Annotation,就是代码里的特殊标记,这些标记可以在遍历,类加载,运行时被读取。
2、Annotation可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的“name=value”对中。
3、一定程度上可以说:框架=注解+反射+设计模式
2、示例:
>生成文档相关的注解,如@author @version等
>在编译时进行格式检查(JDK内置的三个基本注解)
>@Override:限定重写父类方法,该注解只能用于方法
>@Deprecated:用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
>@SuppressWarnings:抑制编译器警告
>跟踪代码依赖性,实现替代配置文件功能
3、自定义注解:
>参照SuppressWarnings定义:
>定义新的Annotation类型使用@interface关键字,并且自动继承了java.lang.annotation.Annotation接口
>Annotation的成员变量在Annotation定义中以无参数方法的形式来声明,方法名和返回值定义了该成员的名字和类型
>可以在定义成员变量时指定初始值,使用default关键字
>如果只有一个参数,建议参数名为value
>如果自定义注解没有成员,表明是一个标识作用。
--->注意:自定义注解必须配上注解的信息处理流程(使用反射)才有意义
>JDK中的四个元注解:对现有的注解进行解释说明的注解(前面两个常用)
>Retention:指定所修饰的Annotation的生命周期:SoURCE\CLASS(默认行为〉\RUNTINE只有声明为RUNTIME生命周期的注解,才能通过反射获取。
>Target:指定可以修饰什么结构
>Documented:用了可以被javadoc解析时
>Inherited:被修饰的Annotation具有继承性
--->通过反射获取注解信息
4、JDK8.0中注解的新特新:可重复注解、类型注解
>可重复注解:
>在MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
>MyAnnotation的Target和Retention等元注解要和MyAnnotations相同。
>类型注解:
>@target中的ElementType.TYPE_PARAMETER:表示该注解能卸载类型变量的声明语句中(如:泛型声明)
>@target中的ElementType.TYPE_USE:表示该注解能卸载使用类型的任何语句中
⑨Java集合:
1、概述:Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
1、集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储
2.1 数组在存储多个数据方法的特点:
>一旦初始化以后,其长度就确定了。
>数组—旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
比如:String[ ] arr;int[] arr1;object[] arr2;
2.2 数组在存储多个数据方面的缺点:
>—旦初始化以后,其长度就不可修改。
>数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高
>获取数组中实际元素的个数,没有线程的属性或方法可用
>存储数据的特点:有序、可重复,特点单一
2、Java集合可分为Collection和Map两种体系
>Collection接口:单列数据,定义了存取一组对象的方法的集合
--->向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
--->Collection常用方法(在具体的结构中实现,Collection是一个接口):
>add(E e):讲元素e添加到集合中
>addAll(Collection c):添加一个集合c到集合中
>isEmpty():判断当前集合是否为空
>clear():清空集合中的元素
>contains(Object obj):判断当前集合中是否包含obj(判断的不是地址是内容)
>containsAll(Collection coll):判断coll中的所有元素是否都存在于当前集合中
>boolean remove(Object obj):删除集合中的指定元素
>removeAll(Collection coll):从当前集合中基础coll中的所有元素
>retainAll(Collection coll):获取当前集合和coll集合的交集,并返回给当前集合
>equals(Object obj):判断当前集合和形参集合元素相同
>hashCode():返回当前对象的哈希值
>toArray():将集合转换成数组,返回是Object类型
>数组---->集合:List<String> list = Arrays.asList(new String[]{"AA","bb"})
注意:写成Arrays.asList(new int[]{123,456})识别为一个元素,长度为1;Arrays.asList(123,456)||Arrays.asList(new Integer[]{123,456},长度就为2
>iterator():返回Iterator接口的实例,使用遍历集合元素
>Iterator迭代器:
>Iterator对象称为迭代器(设计模式的一种),主要用于遍历集合的所有元素
>拥有next()和hashNext()两个方法,一个用来获取值,一个用来判断下一个是否有值
>内部定义了remove(),可以在遍历的时候删除集合中的而元素,此方法不同于集合直接调用remove()
>如果还未调用next()或在上一次调用next方法之后已经调用了remove 方法,再调用remove都会报ILLegalstateException
>foreach:
>for(集合元素类型 局部变量 :集合对象){}
>内部仍然调用了迭代器
》List(“动态数组”):元素有序、可重复的集合
>ArrayList
>作为List接口的主要实现类线程不安全的,效率高;底层使用Object[] elementData存储
>ArrayList底层分析:JDK7和JDK8有一些区别
>JDK7.0
ArrayList list = new ArrayList();//默认长度为10的elementData数组
当超出长度时,默认扩容长度为原来的1.5倍,同时需要将原有数组中的数据复制到新的数组中
特殊情况:
1、如果扩容1.5还不够就直接取当前增加元素的长度
2、当长度超出提前设定好的一个值时,会直接扩容到数组对应类型最大值
结论:建议开发中使用带参的构造器 ArrayList list = new ArrayList(int capacity)
>类似于单例模式中的饿汉式
>JDK8.0
ArrayList list = new ArrayList();//默认没有直接实例化底层数组,创建的是{}
在add时会判断是否实例化,如果没有实例化(第一次调用add)就会max(add的长度,默认值10)作为实例化长度
后续的添加和扩容操作于JDK7.0一样
>类似于单例模式中的懒汉式,延迟了数组的创建,节省内存
>LinkedList
>对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
>LinkedList底层分析
LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。
因为底层不是数组构成不涉及到扩容
其中,Node定义为://提现了LinkedList的双向链表的说法
* private static class Node<E> {
* E item;
* Node<E> next;
* Node<E> prev;
*
* Node(Node<E> prev, E element, Node<E> next) {
* this.item = element;
* this.next = next;
* this.prev = prev;
* }
* }
>Vector
>作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
>Vector底层分析
Vector vector = new Vector();//JDK8.0和7.0一样。内部直接默认定义了数组长度为10
超出长度时,默认扩容为原来的2倍
方法被synchronized关键字修饰,线程安全但效率低
>List接口的方法(Collection接口的子接口):
>void add(int index,object ele):在index位置插入ele元素
>boolean addAll(int index, collection eles):从index位置开始将eles中的所有元素添加进来object get(int index):获取指定index位置的元素
>int indexof(object obj):返回obj在集合中首次出现的位置
>int lastIndexof(object obj):返回obj在当前集合中末次出现的位置
>object remove(int index):移除指定index位置的元素,并返回此元素
>object set(int index,object ele):设置指定index位置的元素为eLe
>list sublist(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
总结:常用方法:增add(object obj)、删remove(int index/Object obj)、改set(int index,Object obj)、
查get(int index)、插add(int index,Object obj)、长度size()、遍历Iterator迭代器方式
--->面试题:ArrayList、LinkedList、Vector三者的异同?
同:三个类都实现了List接口,存储数据的特点相同
不同:
》Set(类似于高中讲的“集合”):元素无序、不可重复的集合
>HashSet
>作为Set接口的主要实现类;线程不安全的;可以存储null值;底层默认长度为16,当使用率找过0.75会扩容到原来的2倍
一、Set:存储无序的、不可重复的
1、无序性://不等于随机性。相比较List是无序的,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值。理解为进教室乱坐座位
2、不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个
>Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
>要求(HashSet\LinkedHashSet):
1、向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
2、重写的hashCode()和equals()尽可能保持一致性;---》保证相等的对象必须具有相等的散列码
--->技巧:在equals()中使用的属性尽量在hashCode()中使用,反之一样。
二、添加元素的过程:以HashSet为例:
>向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出
在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素(需要重写equals()和hashCode()方法):
1、如果此位置没有其他元素,则元素a添加成功。
2、如果此位置有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值;
2.1、如果hash值不相同,则元素a添加成功。
2.2、如果hash值相同,进而需要调用元素a所在类的equals()方法;equals()返回false,则元素a添加陈功,否则失败
---->说明:对于2的两个情况而言,元素a与已经存在指定索引位置上数据以链表的方式存储;
JDK7.0:元素a放到数组中,指向原来的元素
JDK8.0:远在的元素在数组中,指向元素a ---(7上8下)
>HashSet底层=数组+链表 (JDK7.0是,8.0之后变了)
三、重写equals()和hashCode():
>存放数据的类需要重写equals()和hashCode()方法
>在重写的hashCode()方法中:为什么有31这个数字?
>选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
并且31只占用5bits,相乘造成数据溢出的概率较小。
>31可以由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化。(提高算法效率)
>31是一个素数,紊数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结果只能被紊数本身和被乘数还有1来整除!(诚少冲突)
>LinkedHashSet
>是HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
>在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据的地址
>优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
>TreeSet
>可以按照添加对象的指定属性,进行排序
>向TreeSet中添加的数据,要求是相同类的对象
>两种排序方式:自然排序和定制排序
>自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
>定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()
>底层是红黑树
>Map(映射)接口:双列数据,保存具有映射关系"key-value"的集合
--->Map结构的理解:
>Map中的key:无序的、不可重复的,使用Set存储所有的key
--->key所在的类要重写equals()和hashCode()(以HashMap为例)
>Map中的value:无序的、可重复的,使用Collection存储所有的value
--->value所在类要冲写equals()
>一个键值对:key-value构成了一个Entry对象
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
>HashMap
>作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
>HashMap的底层结构:
>数组+链表 (JDK7.0之前)
>数组+链表+红黑树 (JDK8.0)
1、HashMap的底层实现原理?
以JDK7.0为例说明:
HashMap map = new HashMap();在实例化以后,底层创建了长度是16的一维数组Entry[] table,如果小于16默认16
...可能已经执行过多次put...
map.put(key1.value1);
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到Entry数组中的存放位置
1、如果此位置上的数据为空,此时的key1-value1添加成功
2、如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表的形式存在)),比较key1和已经存在的一个或多个数据的哈希值
2.1、如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功
2.2、如果key1的哈希值与已经存在的某一个数据的哈希值相同,调用key1所在类的equals()比较:
2.2.1、如果equals()返回false:此时key1-value1添加成功
2.2.2、如果equals()返回true:使用value1替换相同key的value值
补充:关于2.1和2.2.1:此时key1-value1和原来的数据以链表的方式存储
在不断添加过程中,会涉及到扩容问题,默认的扩容方式:当超出临界值(且要存放的位置非空),扩容为原来容量的2倍,并将原有的数据复制过来
JDK8.0相较于JDK7.0的不同:
1、new HashMap():底层没有创建一个长度为16的数组
2、JDK8.0底层的数组是:Node[],而非Entry[]
3、首次调用put()方法时,底层创建长度为16的数组
4、(核心)JDK7.0底层结构只有:数组+链表,JDK8.0底层结构:数组+链表+红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组长度 > 64 时,
此时此索引位置上的所有数据改为使用红黑树存储(有序二叉树)
1.2、谈谈你对HashMap中的put/get方法的认识?如果了解再谈谈HashMap的扩容机制?
默认大小时多少?什么是负载因子(或填充比)?什么是吞吐临界值?
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量:16
DEFAULT_LOAD_FACTOR : HashMap的默认加载因子:0.75
threshold : 扩容的临界值; threshold = 容量*加载因子 大于临界值会扩容
TREEIFY_THRESHOLD : Bucket中链表长度大于该默认值,转化为红黑树(还要node长度大于64):8
MIN_TREEIFY_CAPACITY : 桶中的Node倍树化时最小的hash表容量:64
>LinkedHashMap
>是HashMap的子类
>保证在遍历Map元素时,可以按照添加的顺序实现遍历。
>原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素
>对于频繁的遍历操作,此类执行效率高于HashMap
>LinkedHashMap的磁层实现原理(了解)
//HashMap.putVal中的newNode被LinkedHashMap重写
//重写中就使用了Entry<K,V>类,继承了Node<K,V>
static class Entry<K,V>extends HashMap.Node<K,V>{
Entry<K,V> before,after;//能够记录添加的元素的先后顺序
Entry (int hash,K key, v value,Node<K,V> next){
super(hash,key, value, next);
}
}
>Hashtable
>作为Map古老的实现类;线程安全,效率低;不能存储null的key和value
>Properties
>是Hashtable的子类,常用来处理配置文件;key和value都是String类型
>SortedMap:是Map的一个子接口
>TreeMap
>保证按照添加的key-value进行排序,实现排序遍历--->考虑key的自然排序或者定制排序
>底层使用红黑树
>Map中定义的方法:
添加、删除、修改操作:
Object put(object key,0bject value):将指定key-value添加到(或修改)当前map对象中
void putALl (Map m):将m中的所有key-value对存放到当前map中
Object remove(0bject key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据
元素查询的操作:
Object get(object key):获取指定key对应的vaLue
boolean containsKey(object key):是否包含指定的key
boolean containsvalue(object value):是否包含指定的value
int size():返叵map 中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(object obj):判断当前map和参数对象obj是否相等
元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values():返回所有vaLue构成的coLlection集合
Set entrySet():返回所有key-vaLue对构成的Set集合
总结:常用方法:
添加put(object key,0bject value)、删除remove(0bject key)、修改put(object key,0bject value)
查询get(object key)、长度size()、遍历keySet()\values()\entrySet()
----->面试题:
1、HashMap的底层实现原理?
2、HashMap和Hashtable的异同?
3、CurrentHashMap与Hashtable的异同?(暂时不了解)
3、Collections工具类:
>常用方法(几乎都是static):
>reverse(List):反转List中元素的顺序
>shuffLe(List):对List集合元素进行随机排序
>sort(List):根据元素的自然顺序对指定List集合元素按升序排序
>sort(List,Comparator):根据指定的Comparator产生的顺序对List 集合元素进行排序swap(List,int,int):将指定list集合中的i处元素和j处元素进行交换
>object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
>object max(collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
>object min(colLection)
>object min(collection,Comparator)
>int frequency(collection,object):返回指定集合中指定元素的出现次数
>void copy(List dest,List src):将src中的内容复制到dest中
>boolean replaceAll(List list,object oldvVal,object newVal):使用新值替换List对象的所有旧值
>Collections常用方法:同步控制
>提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,
从而解决多线程并发访问集合时的线程安全问题
⑩泛型(Generic):
1、概念:
>泛型是JDK5.0新增的特性
>在集合中使用泛型,可以防止集合类型不安全,出现类型转换异常(直接避免类型强转)。
2、在集合中使用泛型:
>集合接口或集合类在JDK5.0时都修改为带泛型的结构
>在实例化集合类时,可以指明具体的泛型类型
>指明完以后,在集合类或接口中凡是定义类或接口时,内部结构使用到类的泛型位置,都指定为实例化的泛型类型
比如:add(E e) ----》实例化以后 : add(Integer e)
>注意点:泛型只能使用类,不能使用基本数据类型,要使用基本数据类型的话需要转化成他们的包装类
>如果实例化时,没有指明泛型的类型,默认类型为Java.lang.Object类型
//JDK7.0以后的特性:类型推断,可以在后面new的地方不用写了
//Map<String,Integer> map = new HashMap<>();
--->泛型方法
要在方法声明时当作关键字给方法加一个泛型,以表明这是一个泛型方法
泛型方法可以声明为静态的。原因:泛型参数实在调用方法时确定的,并非时在实例化类时确定的
在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
换句话说,泛型方法所属的类是不是泛型类都没有关系
--->泛型类、接口
指定了泛型但是未实例化泛型类型,默认为Object类型
如果定义了类是带泛型的,建议在实例化的时候要指明类的泛型
定义了子类SubOrder继承了Order<Integer>,实例化子类对象时,不需要再指明泛型类型
3、泛型注意点:
1.泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
2.泛型类的构造器如下:public GenericClass()。
而下面是错误的: public GenericClass<E>()
3.实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4.泛型不同的引用不能相互赋值。
>尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
5.泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
6.如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
7. jdk1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
8.泛型的指定中不能使用基本数据类型,可以使用包装类替换。
9.在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
10.异常类不能是泛型的
11.不能使用new E[]。但是可以:E[]elements =(E[])new Object[capacity];
参考:ArrayList源码中声明:Object[]elementData,而非泛型参数类型数组。
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
4、泛型在继承方面的提体现:
1、类A是类B的父类,G<A>和G<B>不具备子父类关系,二者是并列关系,对象不能相互赋值
补充:类A是类B的父类(接口),A<G>和B<G>的对象可以赋值
5、通配符:
>英文格式:?
>类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
>添加(写入):对于List<?>就不能向其内部添加数据,除了添加null之外(例如:list = list1 ,list是声明List<?> list1是声明的ArrayList
>获取(读取):允许读取数据,读取的数据类型为Object
>有限制条件的通配符的使用
? extends Person: <= Person类 (?是一个范围)
? super Person: >= Person类 (?是一个范围)
①①IO流:
1、java.io.File类:
Java.in.File类:文件和文件目录路径的抽象表示形式,与平台无关
>File能新建、删除、重命名文件和目录,但不能访问文件本身,如果要访问文件本身,必须使用IO流来完成
>想要在Java程序中表示一个真是存在的文件或目录,那么必须要有一个File对象,但是Java程序中的
File对象,可能没有一个真是存在的文件或目录
>File对象可以作为参数传递给流的构造器
1、File实例化:
>new File(String pathname):以路径创建File对象,如果是相对路径则默认当前路径在系统属性user.dir中存储
>new File(String parent,String child):以parent为父路径,child为子路径创建File对象
>new File(File parent,String child):根据一个父File对象和子文件路径创建File对象
2、File的方法:
>File类的获取功能:
>public string getAbsoLutePath():获取绝对路径
>public String getPath() :获取路径
>public string getName() :获取名称
>public string getParent():获取上层文件目录路径。若无,返回nuLL
>public long length():获取文件长度(即:字节数)。不能获取目录的长度。
>public long LastModified() :获取最后一次的修改时间,毫秒值
//如下两个方法适用与文件目录
>public string[] list():获取指定目录下的所有文件或者文件目录的名称数组
>public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
>File类的重命名功能:
>public boolean renameTo(File dest):把文件重命名为指定的文件路径
>例如 file1.renameTo(file2) 文件file1是必须存在的,文件file2不能存在
>File类的判断功能:(前三个比较常用)
>public booLean isDirectory():判断是否是文件目录
>public boolean isFile() :判断是否是文件
>public boolean exists() :判断是否存在
>public boolean canRead() :判断是否可读
>public boolean canwrite() :判断是否可写
>public booLean isHidden() :判断是否隐藏
>创建硬盘中对应的文件或文件目录
>public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
>public boolean mkdir():创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建
>public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
>删除磁盘中的文件或文件目录
删除注意事项:Java中的删除不走回收站
>public boolean delete():删除文件或者文件夹,文件下不能有任何文件或文件夹
2、IO流:
>Java IO原理:
>I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输。
如读/写文件,网络通讯等。
>Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行。
>java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
>流的分类:
>按操作数据单位不同分为:
>字节流(8bit)-(byte-二进制)图片视频等
---》读取用read(byte[] buffer)
---》写入用write(byte[] buffer,0,len)
>字符流(16bit)--(char)适合文本数据,不能处理图片等
---》读取用read(char[] cbuf)
---》写入用write(char[] cbuf,0,len)
>按数据流的流向不同分为:(4个都为抽象基类)(重要)
>输入流
>字节流:InputStream
>使用字节流FileInputStream处理文本文件,可能出现乱码(读在控制台可能会乱码,复制不会,复制在底层还是一样的值)
>InputStream的使用:和其他流程一样,只不过创建的流对象为InputStream
>字符流:Reader
>Reader的使用:
>1、实例化File对象,指明要操作的文件
>2、提供具体的流 FileReader fr = new FileReader(file);
>3、数据的读入:read():返回读入的一个字符,如果达到文件末尾,返回-1
>4、流的关闭操作
>输出流
>字节流:OutputStream
>OutputStream的使用:和其他流程一样,只不过创建的流对象为OutputStream
>字符流:Writer
>输出操作说明:对应的File可以不存在
如果不存在,在输出的过程中会自动创建文件
如果存在:
如果流使用的构造器是FileWriter(file,false)/FileWriter(file):对原文件进行覆盖
如果流使用的构造器是FileWriter(file,true):不会覆盖原文件,而是在其末尾添加
>Writer的使用:
>1、实例化File对象,指明要写出到的文件
>2、提供FileWriter对象,用于数据的写出
>3、写出的操作:write(cbuf,0,len):从读出来的数组中0-len位置的值写入进去
>4、流资源的关闭操作
>结论:
1、对于文本文件(.txt\.java\.c\.cpp),使用字符流处理
2、对于非文本文件(.jpg\.mp3\.mp4\.avi\.doc\.ppt...),使用字节流处理
>按流的角色不同分为:
>节点流:流直接作用在文件上(重要)
>FileInputStream
>FileOutputStream
FIleInputStream和FileOutputStream的使用:
>使用字节流FileInputStream处理文本文件,可能出现乱码
>FileReader
1、read()的理解:返回读入的一个字符,如果达到文件末尾,返回-1
2、异常的处理:为理保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理异常
3、读入的文件一定要存在,否则就会报FileNotFoundException
>Reader的使用:
>1、实例化File对象,指明要操作的文件
>2、提供具体的流 FileReader fr = new FileReader(file);
>3、数据的读入:read():返回读入的一个字符,如果达到文件末尾,返回-1
>4、流的关闭操作
>FileWriter
>输出操作说明:对应的File可以不存在
如果不存在,在输出的过程中会自动创建文件
如果存在:
如果流使用的构造器是FileWriter(file,false)/FileWriter(file):对原文件进行覆盖
如果流使用的构造器是FileWriter(file,true):不会覆盖原文件,而是在其末尾添加
>Writer的使用:
>1、实例化File对象,指明要写出到的文件
>2、提供FileWriter对象,用于数据的写出
>3、写出的操作:write(cbuf,0,len):从读出来的数组中0-len位置的值写入进去
>4、流资源的关闭操作
>处理流:流作用在其他流上(重要)
>缓冲流
>BufferedInputStream
>BufferedOutputStream
>BufferedReader
>多了一个方法:readLine()一次可以读一行,但是读出的数据不能换行,需要手动换行
>BufferedWriter
>多了一个方法:newLine():换行
>处理流之一:缓冲流的使用
>要求:在流资源关闭时先关闭外层的流,再关闭内层的流 (类似于栈)说明:在关闭外层流的同时,内层流也会自动的进行关闭。
关于内层流的关闭,我们可以省略。内层流关闭写出来也不影响
>作用:提高流的读取、写入的速度
>提高速度的原因:
>内部提供了一个缓冲区,长度为8192,当长度达到8192时会自动调用flush()方法
>flush():刷新缓冲区,缓冲区的数据一次性写出
>处理流之二:转换流
1、转换流提供了在字节流和字符流之间的转换,属于字符流
2、Java API提供了两个转换流:
>InputStreamReader:将InputStream转换为Reader,将字节的输入流转换成字符的输入流
>OutputStreamWriter:将Writer转换为OutputStream,将一个字符的输出流转换成字节的输出流
--->new InputStreamReader(fis,"UTF-8")
1\第二个参数指明字符集,具体使用哪个字符集取决于原文件的编码
2\不要第二个参数=使用系统默认的字符集
3、字节流中的数据都是字符时,转成字符流操作更高效。
4、很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
5、字符集
>ASCII:美国标准信息交换码,用一个字节的7位可以表示。
>IS08859-1:拉丁码表,欧洲码表,用一个字节的8位表示。
>GB2312:中国的中文编码表,最多两个字节编码所有字符
>GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
>Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。
>UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。
>其他的一些流:
1、标准输入、输出流:
1.1
System.in:标准的输入流,默认从键盘输入
System.out:标准的输出流,默认从控制台输出
1.2
System类的setIn(InputStream is)/setOut(PrintStream os)方式重新指定输入和输出方式
1.3输入输出的使用
>输入
>创建转换流对象构造器参数为system.in命名为isr---》因为system.in是字节流
>创建BufferedReader构造器参数为isr
>然后具体的输入方法,读取用的是br的readLine()方法
>关闭最外层流,内层流自动关闭
2、打印流
>概念:
提供了一系列重载的print()和println()方法,用于多种数据类型的输出
打印流输出不会抛出IOException异常,有自动的flush功能
PrintStream写入字节,PrintWriter写入字符
System.out返回的是PrintStream的实例
2.1、PrintStream
>使用:
>创建File和FileOutputStream对象,创建PrintStream ps对象构造器传入fos
>再通过System.setOut(ps,true)设置输出方式 ---》第二个参数为aoutFlush
>然后下面的system.out.print/println的输出方式就改为了前面向文件输出的方式
2.2、PrintWriter
3、数据流
3.1 DataInputStream 和 DataOutputStream
3.1.1
DataOutputStream构造器传入FileOutputStream(van.txt)
传入数据通过writeXxx()传入
3.1.2
DataInputStream构造器传入FileInputStream(van.txt)
读取数据通过readXxx()读取,且必须按照输入的顺序的数据类型读取
3.2 为了方便操作Java语言的基本数据类型和String数据,可以使用数据流
4、对象流(重要)
1、对象流:ObjectInputStream 和 ObjectOutputStream --->字节流
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基木类型数据或对象的机制
ObjectOutputStream和ObjectInputStream不能序列化static和ltransient修饰的成员变量
2、作用用于存储和读取基木数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,
也能把对象从数据源中还原回来。
3、要想一个java对象是可序列化的,需要满足相应的要求对象的类需要满足如下的要求,方可序列化
1、实现serializable(一般都是这个)或者externalizable接口
2、当前类提供一个全局常量serialVersionUID---》序列版本号,虽然没写可以自动生成,但是如果类有变动常量也会变动,会导致无法反序列化
3、除了当前Person类需要实现serializable接口之外,还需要保证其内部所有属性也必须是可序列化的
(默认情况下,基本数据类型是可序列化的)(内部属性可能是自定义类,那么自定义类也要是可序列化的)
5、随机(任意)存取文件流
5.1、 RandomAccessFile声明在java.io包下,但直接继承于java.lang.Object类。
并且它实现了Datalnput、DataOutput这两个接口,也就意味着这个类既可以读也可以写。
5.2、RandomAccessFile类支持“随机访问”的方式,程序可以直接跳到文件的任意地方来读、写文件
>支持只访问文件的部分内容
>可以向已存在的文件后追加内容
5.3、RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置。
RandomAccessFile类对象可以自由移动记录指针:
>long getFilePointer():获取文件记录指针的当前位置
>void seek(long pos):将文件记录指针定位到pos位置
5.4、RandomAccessFile的使用:
1、RandomAccessFile构造器
new RandomAccessFile(new File("iofile//messi.jpg"),"r")
第二个参数代表可读可写:有r、rw、rwd、rws
2、 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建
如果写出到的文件存在,则会对原有文件内容进行覆盖(默认情况下,从头覆盖)
3、可以通过相关的操作,实现RandomAccessFile插入的效果(效率比较差)
>注意
IDEA:
如果使用单元测试方法,相对路径基于当前的Module的。
如果使用main()测试,相对路径基于当前Project的。
Eclipse:都是基于当前project
3、NIO:后面再讲
>无阻塞的数据传输
>IO的read()是有阻塞的
>NIO2中---JDK7.0提供:
>Path类:
>替换原有的File类
>有更多的方法,会抛异常,File只会返回错误
>Paths类提供的静态get()方法用来获取Path对象:
>static Path get(String first,String...more):用于将多个字符串串联成路径
>static Path get(URL url):返回指定url对应的path路径
>path类相比于File类有很多方法
>Files工具类:
JDK7.0提供,操作文件或文件目录的工具类
①②网络编程
1、概述:
>Java是 Internet上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。
>Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由JVM进行控制。
>并且Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
>网络通信协议模型:
OSI参考模型: TCP/IP参考模型 TCP/IP各层对应协议
>应用层 >应用层 HTTP、HTTPS、FTP
>表示层 Telnet、DNS
>会话层
>传输层 >传输层 TCP、UDP
>网络层 >网络层 IP、ICMP、ARP
>数据链路层 >物理+数据链路层 LINK
>物理层
一、网络编程中有两个主要的问题;
1、如何涯确地定位网络上一台或多台主机;定位主机上的特定的应用
2、找到主机后如何可靠高效地进行数据传输
二、网络编程中的两个要素:
1、对应问题一:IP和端口号
2、对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理层)
三、通信要素一:IP和端口号
1、IP:唯一的表示Internet上的计算机
2、在Java中使用InetAddress类代表IP
3、IP分类:IPv4和Ipv6;
4、域名
5、本地回路地址:127.0.0.1 对应着:localhost
6、如何实例化InetAddress:两个方法:getByName(String host)、getLocalHost;
两个常用方法:getHostName() / getHostAddress()
7、端口号:正在计算机上运行的进程
要求:不同的进程有不同的端口号
范围:被规定为一个16位的整数:0-65535
8、端口号与IP地址的组合得出一个网络套接字:Socket
四、通信要素二:网络通信协议
TCP协议:
>使用TCP协议前,须先建立TCP连接,形成传输数据通道>
>传输前,采用“三次握手”方式,点对点通信,是可靠的
>TCP协议进行通信的两个应用进程:客户端、服务端。
>在连接中可进行大数据量的传输
>传输完毕,需释放已建立的连接,效率低
TCP协议的使用:
客户端:
1、创建Socket对象,指明服务器端的IP和端口号
2、获取一个输出流,用于输出数据
3、写出数据的操作
4、资源关闭
服务器端
1、创建服务器端的ServerSocket,指明自己的端口号
2、调用accept()表示接收来自于客户端的socket
3、获取输入流
4、读取输入流中的数据
5、资源关闭
UDP协议:
>将数据、源、目的封装成数据包,不需要建立连接>
>每个数据报的大小限制在64K内
>发送不管对方是否准备好,接收方收到也不确认,故是不可靠的>
>可以广播发送
>发送数据结束时无需释放资源,开销小,速度快
>UDP的使用:
>类 DatagramSocket和 DatagramPacket实现了基于UDP 协议网络程序。
>UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
>DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
>UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。如同发快递包裹一样。
五、URL网络编程
1、URL(Uniform Resource Locator):统一资源定位符,它表示Internet上某资源的地址。
2、它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
3、通过URL我们可以访问Internet上的各种网络资源,比如最常见的www,ftp站点。
浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源。
4、URL的基本结构由5部分组成:
<传输协议>:/<主机名>:<端口号><文件名>#片段名?参数列表>例如:
http://192.168.1.100:8080/helloworld index.jsp#a?username=shkstart&password=123>#片段名:即锚点,例如看小说,直接定位到章节
>参数列表格式:参数名=参数值&参数名=参数值....
5、URL常用方法:
>public String getProtocol()获取该URL的协议名
>public String getHost( ) 获取该URL的主机名
>public String getPort() 获取该URL的端口号
>public String getPath( ) 获取该URL的文件路径
>public String getFile() 获取该URL的文件名
>public String getQuery() 获取该URL的查询名
6、通过URL下载文件:
>是通过服务器下载,必须在服务器打开或者能访问到时才能下载
1、创建URL对象,通过构造器获取需要下载东西的位置
2、通过url.openConnection获取连接,返回一个HttpURLConnection对象urlConnection
3、urlConnection.connect()获取连接
4、urlConnection.getInputStream(),然后通过流传输写入
5、关闭资源
①③Java反射机制
框架 = 反射 + 注解 + 设计模式
一、概念
>Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
>加载完类之后,在堆内存的方法区中就产生了一个class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。
我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
>Java反射机制提供的功能
>在运行时判断任意一个对象所属的类
>在运行时构造任意一个类的对象
>在运行时判断任意一个类所具有的成员变量和方法
>在运行时获取泛型信息
>在运行时调用任意一个对象的成员变量和方法
>在运行时处理注解
>生成动态代理
疑问?:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?
答:建议:直接new的方式
什么时候会使用:反射的方式。反射的特征:动态性
在开发中有时会出现编译时不明确知道要造的是哪个对象
疑问?:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
答:不矛盾
二、关于java.lang.Class类的理解
1、类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,
此过程就称为加载。加载到内存中的类,我们就称为运行时类,此时运行时类,就作为Class的一个实例
2、换句话说,Class的实例就对应着一个运行时类
3、加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式获取此运行时类。
4、获取Class的实例方式
方式一:调用运行时类的属性:.class
方式二:通过运行时类的对象
方式三:调用Class的静态方法:forName(String classPath)(使用最多)
方式四:使用类的加载器:(了解)
5、关于反射创建对象、调用方法、属性、构造器、泛型
1、查看IDEA中的Reflection中的ReflectionTest
2、调类的各种东西:
获取属性结构:
getFields():获取当前运行时类及其父类中为public的属性
getDeclaredFields():获取当前运行时类当中声明的所有属性,不包含父类
还可以获取具体的权限修饰符、数据类型、变量名
获取方法:
//getMethods():获取当前运行时类及其所有父类中为public的方法
Method[] methods = clazz.getMethods();
//getDeclaredMethods():获取当前运行时类当中声明的所有方法,不包含父类
Method[] declaredMethods = clazz.getDeclaredMethods();
还可以获取:方法声明的注解、权限修饰符、返回值类型、方法名、形参列表、抛出异常throws
获取构造器结构:
//getConstructors():获取当前运行时类中为public的构造器
Constructor[] constructors = clazz.getConstructors();
//getDeclaredConstructors():获取当前运行时类当中声明的所有构造器,不包含父类
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
获取运行时类的父类及其泛型:
获取运行时类的父类:clazz.getSuperclass();
获取运行时类的带泛型的父类:clazz.getGenericSuperclass();
获取运行时类的带泛型的父类的泛型:
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
获取运行时类的接口:clazz.getInterfaces();
获取运行时类所在的包:clazz.getPackage();
获取运行时类的注解:clazz.getAnnotations();
3、调用运行时类中的指定结构(重要):
---》都要先创建运行时类
1、开发中常用的操作运行时类的指定的属性:
//创建运行时类的对象
Person p = clazz.getDeclaredConstructor().newInstance();
//1、getDeclaredField(String fieldName):获取运行时类中指定的属性
Field name = clazz.getDeclaredField("name");
//2、保证当前属性是可访问的 .setAccessible(true)
name.setAccessible(true);
//3、获取、设置指定对象的属性值
name.set(p,"Van");
System.out.println(name.get(p));
---》静态属性:调用静态属性为运行时类(null)也是可以运行的没有影响
2、开发中常用的操作运行时类的指定的方法:
/*
1、获取指定的某个方法
getDeclaredMethod():参数1;指明获取方法的名称;参数2:指明获取的方法的形参列表
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2、保证当前方法是可访问的
show.setAccessible(true);
/*
3、调用invoke()去执行
invoke():参数1:方法的调用者;参数2:给方法形参复制的实参
invoke()的返回值即会对应类中调用的方法的返回值
调用静态方法参数为运行时类(null)也是可以运行的没有影响
*/
Object chn = show.invoke(p, "CHN");
System.out.println(chn);
3、开发中常用的操作运行时类的指定的构造器:
//创建运行时类的对象
Person p = clazz.getDeclaredConstructor().newInstance();
//1、获得指定的运行时类的构造器
//getDeclaredConstructor(String.class):参数:指明构造器的参数列表
Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
//2、保证当前方法是可访问的
constructor.setAccessible(true);
//3、调用此构造器创建此运行时类的对象
Person leo = constructor.newInstance("Leo");
System.out.println(leo);
6、类加载器
调用系统类接载其的getParent():获取扩展类加载器
调用扩展类加载器的getParent():无法获取引导类加载器
引导类加载器主要负责加载java的核心类胡,无法加载自定义类
7、哪些类型可以有Class对象?
>Class本身
>class:外部类、所有内部类
>interface
>[]数组
>enum:枚举类
>annotation:注解@interface
>primitive type:基本数据类型
>void
8、通过反射创建对应的运行时类的对象
newInstance():调用方法创建运行时类的对象,内部调用了空参构造器
JDK9.0开始这个这个方法被弃用(过时)
Person o = clazz.newInstance();
JDK9.0之后用这个方法
clazz.getDeclaredConstructor().newInstance();
要求:
1、运行时类必须提供空参的构造器
2、空参的构造器的访问权限得够,通常设置为:public
在javabean中要求提供一个public的空参构造器。原因:
1、便于通过反射,创建运行时类的对象
2、便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
①④:JDK8.0的新特性:
1、Lambda表达式
1.1、什么是lambda表达式:
>Lambda是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。
使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
1.2、Lambda表达式的使用
1、举例 (o1,o2) -> Integer.compare(o1,o2) 或者
(o1,o2) -> {
Integer.compare(o1,o2);
};
2、格式:
-> :lambda操作符 或 箭头操作符
->左边 :lambda形参列表 (其实就是接口中的抽象方法的形参列表)
->右边 :lambda体 (其实就是重写的抽象方法的方法体)
3、lambda表达式的使用:6种情况
语法格式一:无参无返回值的
语法格式二:Lambda需要一个参数,但是没有返回值。
语法格式三:数据类型可以省略,因为可由编辑器推断得出,称为“类型推断”
语法格式四:lambda 若只需要一个参数时,参数的小括号可以省略
语法格式五:lambda 需要两个或以上的参数,多条执行语句,并且可以由返回值
语法格式六:当lambda 体只有一条语句时,return 与 大括号若由,都可以省略
--->总结:
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,小括号也可以省略
->右边:lambda体应该使用一对{}包裹,如果lambda体只有一条执行语句(可能是return),可省略{} && return
4、lambda表达式的本质:作为接口的实例
>必须是函数式接口
>如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口
>详细见MyFunctionInterface.java
2、函数式(Functional)接口
1、只包含一个抽象方法的接口,称为函数式接口。
>你可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常(即:非运行时异常),
那么该异常需要在目标接口的抽象方法上进行声明)。
>我们可以在一个接口上使用@Functionallnterface注解,这样做可以检查它是否是一个函数式接口。
同时 javadoc也会包含一条声明,说明这个接口是一个函数式接口。
>在java.util.function包下定义了Java8的丰富的函数式接口
2、Java内置四大核心函数式接口
函数式接口 参数类型 返回类型 用途
1、Consumer<T> T void 对类型为T的对象应用操作
消费型接口 方法:void accept(T t)
2、Supplier<T> 无 T 返回类型为T的对象
供给型接口 方法:T get()
3、Function<T,R> T R 对类型为T的对象应用操作,并返回结果
函数型接口 方法:R apply(T t)
4、Predicate<T> T boolean 确定T的对象是否满足约束
断定型接口 方法:boolean test(T t)
3、方法引用与构造器引用
3.1、方法引用:
>和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属的类的类型
>例: Supplier<Employee> sup2 = Employee::new;
3.2、构造器引用:
>可以把数组看作是一个特殊的类,则写法与构造器一致了
4、强大的Stream API
1、概念:Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。
简言之,Stream API提供了一种高效且易于使用的处理数据的方式。
2、Stream和Collection集合的区别:
>Collection是一种静态的内存数据结构,Stream是有关计算的
>Collection是主要面向内存,存储在内存中;Stream是面向CPU,通过CPU实现计算
3、Stream操作的三个步骤:
1、创建Stream--->实例化
>一个数据源(集合、数组),获取一个流
>Stream实例化的4种方式:
>方式一:通过集合创建
List<Employee> employees = EmployeeData.getEmployees();
//default Stream<E> stream():返回一个顺序流
Stream<Employee> stream = employees.stream();
//default Stream<E> parallelStream():返回一个并行流
Stream<Employee> employeeStream = employees.parallelStream();
>方式二:通过数组创建
Arrays.stream(arr);返回一个流
>方式三:通过Stream的of()
Stream.of(1, 2, 3, 4, 5, 6, 7);返回一个流
>实例化方式四:创建无限流
迭代
public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f)
生成
public static<T> Stream<T> generate(Supplier<T> s)
2、中间操作--->过滤、映射等
>一个中间操作链,对数据源的数据进行处理
1-筛选与切片
filter(Predicate p):接收Lambda,从流中排除某些元素
distinct():筛选,通过流所生成元素的 hashCode()和 equals()去除重复元素
limit(long maxsize):截断流,使其元素不超过给定数量
skip(long n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
2-映射
map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3-排序
sorted():产生一个新流,共中按自然顺序排序
sorted(Comparator com):产生一个新流,其中按比较器顺序排序
3、终止操作(终端操作)
>一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
1-匹配与查找
allMatch(Predicate p):检查是否匹配所有元素
anyMatch(Predicate p):检查是否至少匹配一个元素
noneMatch(Predicate p):检查是否没有匹配所有元素
findFirst():返回第一个元素
findAny():返回当前流中的任意元素
count():返回流中元素的总个数
max():返回流中最大值
min():返回流中最小值
forEach(Consumer c)——内部迭代,以前使用的iterator是外部迭代器
2-归约
reduce(T lden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T
reduce(Binaryoperator b):可以将流中元素反复结合起来,得到一个值。返回Optional<T>
3-收集
collect(Collector c):将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例:
>toList():把流中元素收集到List
>toSet():把流中元素收集到Set
>toCollection():把流中元素收集到创建的集合
注意:
>stream自己不会存储元素。
>stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
>stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
5、Optional类
1、概念:
>Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,
表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
>Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
2、方法:
创建Optional类对象的方法:
>Optional.of(T t):创建一个Optional 实例,t必须非空;
>Optional.empty():创建一个空的Optional实例
>Optional.ofNullable(T t):t可以为null
判断Optional容器中是否包含对象:
>boolean isPresent():判断是否包含对象
>void ifPresent(Consumer<? super T> consumer):如果有值,就执行Consuner
接口的实现代码,并且该值会作为参数传给它。
获取Optional容器的对象:
>Tget():如果调用对象包含值,返回该值,否则抛异常
>T orElse(T other):如果有值则将其返回,否则返回指定的other对象。
>T orElseGet(Supplier<? extends T>other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
>T orElse Throw(Supplier<? extends X>exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
JDK:
JDK中主要的包:
java.lang:包含一些Java语言的核心类,String、Math等
java.net:包含执行与网络相关的操作的类和接口
java.io:输入输出功能的类
java.util:一些实用工具类、接口的集合框架类等
java.text:java格式化相关的类
java.sql:java进行JDBC数据库编程的相关类和接口
java.awt:构成抽象窗口工具集(abstract window toolkits)的多个类
JDK5:
1、新增了可变个数的形参方法
2、新增foreach用于遍历数组和集合
MVC设计模式:
1、将整个程序分为三个层次:视图模型层、控制器层、数据模型层。
2、数据模型层:
数据对象封装 model.bean/domain
数据库操作类 model.dao
数据库 model.db
3、视图模型层:
相关工具类 view.utils
自定义view view.ui
4、控制器层:
应用界面相关 controller.activity
存放fragment controller.fragment
显示列表的适配器 controller.adapter
服务相关的 controller.service
抽取的基类 controller.base
JavaBean:
是一种Java语言写成的可重用组件
特点:类是公共的、有一个无参的公共构造器、有属性且有get、set方法。满足这三个条件就称为JavaBean
文档注释编写:
/**
* @Description
* @author Van
* @version
* @date ${date}${time}
*
*/
/**
* @Description
* @author van
* @date ${date}${time}
* ${tags}
*/
完结!Van!