类变量
什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
…
下面来看一段代码
1 | class Child{ |
静态域因jdk版本不同,可能存放在堆里,也有可能在方法区里
但可以肯定的是,静态变量被所有对象共享,而且在类加载的时候就生成了(new的时候加载一次,且只加载一次)
关于上面代码里的count变量,也就是static变量
在jdk8以前,会认为是在这个方法区里面,但jdk8以后静态域放在了堆里面
当类被加载的时候,会在堆里生成一个class对象,而static变量就被保存在class实例的尾部,所以认为静态变量在堆里面
这里顺便提一句,局部变量在使用完之后内存会被释放,但静态变量会保留内存,直到程序结束
访问类变量
- 类名.类变量名
- 对象名.类变量名
这里更推荐使用前者,看起来更规范一些
而实例变量不能通过前者访问
因为类变量随着类的加载而创建,所以没有创建对象实例也可访问类变量
因此,类变量的生命周期是随类的加载开始,随着类消亡而销毁
同时,类变量的访问也要遵守修饰符的访问权限,和普通属性是一样的
什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量
例如:定义学生类,统计所有学生共交多少钱
类变量与实例变量(普通变量)的区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的
加上static的变量称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
类变量就先记录这么多吧…
类方法
类方法也叫静态方法
当方法使用了static修饰后,该方法就是静态方法,静态方法就可以访问静态属性/变量
静态方法不能调用非静态变量
形式如下:
- 访问修饰符 static 数据返回类型 方法名(){}
- static 访问修饰符 数据返回类型 方法(){}
这里也是为了规范,推荐使用前者
类方法的调用
- 类名.类方法名
- 对象名.类方法名
跟前面的类变量一样,要满足访问修饰符的访问权限和范围
使用场景
我们可以发现在工具类中提供的方法基本都是静态方法
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。
在开放中往往将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等…
如果我们希望不创建实例,也可以调用某个方法,这时候静态方法就是一个很好的使用场景
注意事项
- 类方法也和类变量一致,随着类的加载而加载,将结构信息存储在方法区
- 类方法中无
this的参数,而普通方法中隐含着this的参数 - 类方法可以通过类名调用,也可以通过对象名调用,不能通过类名调用
- 类方法中不允许使用和对象有关的关键字,比如
this和super。但普通方法可以使用 - 类方法(静态方法)中只能访问静态变量和静态方法,但普通方法即可以访问普通变量和方法,也可以访问静态变量和方法(但必须遵守访问权限)
理解main方法语法
深入理解main方法
解释main方法的形式
1 | public static void main(String[] args) {} |
main方法时虚拟机调用的- Java虚拟机需要调用类的
main()方法,所以该方法的访问权限必须是public - Java虚拟机在执行
main()方法时不必创建对象,所以该方法必须是static - 该方法接收
String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数 - main的形参,就是把
Java 运行的类名 参数1 参数2 参数3中最后面的参数形成一个数组带进去,而且可以在主方法里再取出来
同上面的类方法,在main()方法中,我们可以直接调用main()方法所在类的静态方法或静态属性,但是,不能直接访问该类的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
main动态传值
刚刚上面讲的Java 运行的类名 参数1 参数2 参数3是在命令行中的传值方法
下面我们用下面这段代码来说一下idea里怎么进行传递参数
1 | public class Main { |
我们在idea右上角选择Edit Configurations

在Program arguments里写入参数即可

如图,这里我们传入杭州 宁波 温州看看效果

运行后可以看到我们成功传入了参数

main方法就到这…
代码块
基本介绍
代码块又称为初始化块,属于类中的成员[即 是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法
1 | [修饰符]{ |
语法说明:
- 修饰符可选,要写的话,也只能写
static - 代码块分为两类,使用
static修饰的叫静态代码块,没有static修饰的,叫普通代码块 - 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
;号可以写上,也可以省略
代码块的好处:
- 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作,顺序优先于构造器
- 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
细节(重点)
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。
类什么时候被加载
①创建对象实例时(new)
②创建子类对象实例,父类也会被加载
③使用类的静态成员时(静态属性、静态方法)
普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行
静态代码块和静态属性初始化的优先级一样,按他们定义的顺序调用,普通代码块和普通属性初始化的优先级也一样,但是会先调用静态,其次按他们定义的顺序调用,最后调用构造器,总结一下就是静态>普通>构造器
构造器的最前面其实隐含了super()和调用普通代码块(先父类按顺序,再子类按顺序)
调用顺序
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
④父类的构造方法
⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
⑥子类的构造方法
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
单例设计模式
什么是设计模式
- 静态方法和属性的经典使用
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式
什么是单例模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
单例模式有两种方式:
①饿汉式
②懒汉式
单例模式应用实例-饿汉式
实现步骤
- 构造器私有化==>防止直接
new - 类的内部创建静态对象
- 向外暴露一个静态的公共方法
getInstance - 代码实现
这里解释一下,之所以要将getInstance方法设置为静态方法,是因为避免去new一个对象,那又因为要让getInstance这个静态方法能够使用到类内部创建的对象,所以需要将对象设置为静态的
下面看一下具体的代码
1 | public class SingleTon01 { |
题外话
为什么叫饿汉式?
因为不管你有没有使用这个对象,它都已经帮你创建好了一个对象,所以叫饿汉式…相反,不去用它,它就不会创建,被称为懒汉式…
单例模式应用实例-懒汉式
饿汉式不使用对象却也会创建对象,造成了一种资源的浪费,而懒汉式就解决了这一个问题
实现步骤
- 仍然构造器私有化
- 定义一个
static静态属性对象 - 提供一个
public的static方法,可以返回一个Cat对象 - 懒汉式,只有当用户使用
getInstance时,才返回cat对象,从而再次调用时,会返回上次创建的Cat对象
从而保证了单例
1 | public class SingleTon02 { |
总结单例设计模式
饿汉式VS懒汉式
- 最主要的区别就是创建的时机不同,饿汉式在类加载就创建了对象实例,而懒汉式是在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题(后续介绍)
- 饿汉式存在浪费资源的可能
- 在
javaSE标准类中,java.lang.Runtime就是经典的单例模式
final关键字
基本介绍
final可以修饰类、属性、方法和局部变量
使用场景
当不希望类被继承时,可以用
final修饰1
2final class A{} //在不想被继承的类前加final
class B extends A{} //此处会报错,无法继承A类当不希望父类的某个方法被子类覆盖/重写时,可以用
final关键字修饰具体格式为
访问修饰符 final 返回类型 方法名1
2
3
4
5
6
7
8
9class C {
public final void hi(){} //这里使用final修饰方法
}
class D extends C{
public void hi() { //此处会报错,只能继承但不能重写C类的hi方法
System.out.println("重写C类的hi方法");
}
}当不希望类的某个属性的值被修改,可以使用
final修饰1
2
3
4
5
6
7
8
9public class Final01 {
public static void main(String[] args) {
E e = new E();
e.n1=888; //报错,无法修改
}
}
class E{
public final int n1=999; //这里用final修饰变量
}当不希望某个局部变量被修改,可以用
final修饰1
2
3
4
5
6
7class F{
public void cry(){
final double Num=0.01; //finall修饰局部变量
Num=0.9; //报错,无法修改
System.out.println("Num= "+Num);
}
}
注意事项
final修饰的属性又叫常量,一般用XX_XX_XX来命名final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一①定义时:如
public final double TAX_RATE=0.08;②在构造器中
③在代码块中
1
2
3
4
5
6
7
8
9public final double TAX_RATE=0.01; //第一种
public final double TAX_RATE2; //第二种
public final double TAX_RATE3; //第三种
public AA(){ //第二种:在构造器中赋初值
TAX_RATE2=1.1;
}
{ //第三种:在代码块中赋初值
TAX_RATE3=2.2;
}如果
final修饰的属性是静态的,则初始化的位置只能是①定义时
②在静态代码块,不能再构造器中赋值
1
2
3
4
5
6
7
8
9
10public static final double TAX_RATE=0.01;
public static final double TAX_RATE2; //报错
public static final double TAX_RATE3;
public BB(){
TAX_RATE2=1.1; //构造器不能赋值(静态)
}
static {
TAX_RATE3=2.2; //静态代码块可以赋值
}final类不能继承,但是可以实例化对象1
2
3
4
5
6public class FinalDetail01 {
public static void main(String[] args) {
CC cc = new CC(); //实例化对象不会报错
}
}
final class CC{} //final类如果类不是final类,但是含有
final方法,则该方法虽然不能重写,但是可以被继承1
2
3
4
5
6
7
8
9
10
11public class FinalDetail01 {
public static void main(String[] args) {
new EE().cal(); //可以正常调用
}
}
class DD{
public final void cal(){ //此处含有final方法
System.out.println("cal()方法");
}
}
class EE extends DD{} //可以继承DD一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
final不能修饰构造方法(即构造器)
final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
1
2
3
4
5
6
7
8
9public static void main(String[] args) {
System.out.println(BBB.num); //输出999和BBB被执行
}
class BBB{
public static int num=999;
static{
System.out.println("BBB 的静态代码块被执行");
}
}1
2
3
4
5
6
7
8
9public static void main(String[] args) {
System.out.println(BBB.num); //仅输出999
}
class BBB{
public final static int num=999;
static{
System.out.println("BBB 的静态代码块被执行"); //此句不输出
}
}包装类(Integer,Double,Float,Boolean等都是final),String也是final类
抽象类
引入抽象类
当父类的某些方法,需要声明,但是又不确定如何实现时,可以用abstract关键字来修饰方法,将其声明为抽象方法,那么这个类就是抽象类
具体写法
这里要注意,如果要将方法设为抽象方法,那么需要在类的前面也加上abstract,否则会报错,同时抽象方法没有方法体,否则会报错
具体格式为访问修饰符 abstract 返回类型 方法名(参数列表);
1 | abstract class Animal{ //类的前面加上abstract |
而且,抽象类一般来说会被继承,由其子类来实现抽象方法
使用细节
抽象类不能被实例化
1
2
3
4
5
6public class Abstract02 {
public static void main(String[] args) {
new A(); //此处报错,不能实例化抽象类
}
}
abstract class A{}抽象类不一定要包含
abstract方法,换句话说,抽象类可以没有abstract方法,还可以有实现的方法1
abstract class A{} //用上面的代码来举例,此处并不会报错
1
2
3
4
5abstract class A{
public void hi(){
System.out.println("hi");
}
}一旦类包含了
abstract方法,则这个类必须声明为abstract1
2
3class B{
public abstract void hi(); //报错,因为包含了抽象方法,所以必须是抽象类
}abstract只能修饰类和方法,不能修饰属性和其他1
2
3class C{
public abstract int a; //报错,abstract只能修饰类和方法
}抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等
抽象方法不能有主体,即不能实现(也就是开头所说的不能有方法体)
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为
abstract类1
2
3
4abstract class E{
public abstract void hi();
}
class F extends E{} //这里继承了E抽象类,但没有实现所有的抽象方法,所以会报错1
2
3
4abstract class E{
public abstract void hi();
}
abstract class F extends E{} //那么我们在子类前加上abstract就不会报错1
2
3
4
5
6
7abstract class E{
public abstract void hi();
}
class G extends E{
public void hi() {} //所谓实现方法,就是要有方法体(空也可以)
}抽象方法不能用private、final、static来修饰,因为这些关键字都是和重写相违背的
抽象模板设计
首先我们来看下面一段代码(分别在三个文件中)
1 | public class TestTemplate { |
这里提一句,currentTimeMillis()可以以毫秒为单位获取系统当前的时间,返回long数据类型的数据
我们发现,AA类和BB类有相当一部分是重复的,下面进行改进,体会一下抽象模板设计
1 | public class TestTemplate { |
从上面的实践可以看出来抽象类对代码的复用性和简洁性还是有很大帮助的…
接口
快速入门
看下面的代码模拟一下现实中的USB接口…
1 | public class Interface01 { |
Phone类实现了UsbInterface,即Phone类需要实现UsbInterface接口规定/声明的方法
基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。语法:
1 | interface 接口名{ |
注意:在JDK7.0前,接口里的所有方法都没有方法体,即都是抽象方法。JDK8.0后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
默认实现方法,需要使用default关键字修饰,如下
1 | default public void ok(){ |
注意事项
接口不能被实例化
接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
一个普通类实现接口,就必须将该接口的所有方法都实现
这里有一个快捷键,在下图报错的位置,使用Alt+Enter,选择Implement methods即可快速导入接口中所有要实现的方法

抽象类实现接口,可以不用实现接口的方法
1 | interface IA{ |
一个类同时可以实现多个接口
1 | interface IB{ |
接口中的属性,只能是final的,而且是public static final修饰符
接口中属性的访问形式:接口名.属性名
接口不能继承其他的类,但是可以继承多个别的接口
1 | interface IB{ |
接口的修饰符只能是public和默认,这点和类的修饰符是一样的
接口和继承的区别
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法
子类继承了父类,就自动的拥有父类的功能,如果子类需要扩展功能,可以通过实现接口的方式扩展,可以理解实现接口是对 单继承机制的一种补充
继承是满足is-a的关系,而接口只需满足like-a的关系
接口在一定程度上实现了代码解耦,即:接口规范性+动态绑定机制
接口的多态特性
多态参数,在前面的代码里UsbInterface usb,即可以接收手机对象,又可以接收相机对象,体现了借口多态,且接口类型的变量可以指向实现了接口的对象实例
多态数组,在Usb数组中,存放Phone和Camera对象,
1 | public class InterfacePolyArr { |
多态传递,用下面的例子来说,如果IG继承了IH接口,而Teacher类实现了IG接口,那么,实际上就相当于Teacher类也实现了IH接口
1 | public class InterfacePolyPass { |
内部类
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类,内部类最大的特点就是可以直接访问私有属性,并且可以提现类与类之间的包含关系
基本语法:
1 | class Outer{ //外部类 |
内部类的分类
定义在外部类局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名)
定义在外部类的成员位置上:
- 成员内部类(没用static修饰)
- 静态内部类(使用static修饰)
局部内部类的使用
定义在外部类的局部位置,比如方法中,并且有类名
可以直接访问内外部类的所有成员,包含私有的
1
2
3
4
5
6
7
8
9
10
11
12class Outer02{
private int n1=100;
private void m2(){}
public void m1(){
class Inner02{ //局部内部类
public void f1(){
System.out.println("n1="+n1); //访问了外部类的n1(私有属性)
m2(); //访问了外部类的m2(私有方法)
}
}
}
}不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的。但是可以使用final修,因为局部变量也可以使用final
1
2
3
4
5
6class Outer02{
public void m1(){
public class Inner02{ //报错,局部内部类不能添加访问修饰符
}
}
}1
2
3
4
5
6
7class Outer02{
public void m1(){
final class Inner02{ //此处使用final修饰
}
class Inner03 extends Inner02{} //报错,final修饰后无法继承
}
}作用域:仅仅在定义它的方法或代码块中
局部内部类—访问—>外部类的成员[访问方式:直接访问]
1
2
3
4
5
6
7
8
9
10
11
12class Outer02{
private int n1=100;
private void m2(){}
public void m1(){
class Inner02{ //局部内部类
public void f1(){
System.out.println("n1="+n1); //访问了外部类的n1(私有属性)
m2(); //访问了外部类的m2(私有方法)
}
}
}
}外部类—方法—>局部内部类的成员[访问方式:创建对象,再访问(注意:必须在作用域内)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14class Outer02{
private int n1=100;
private void m2(){}
public void m1(){
final class Inner02{ //局部内部类
public void f1(){
System.out.println("n1="+n1);
m2();
}
}
Inner02 inner02 = new Inner02(); //先创建对象
inner02.f1(); //再访问内部类的成员
}
}外部其他类—不能访问—>局部内部类(因为局部内部类地位是一个局部变量)
如果外部类和局部内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
1
2
3
4
5
6
7
8
9
10
11
12class Outer02{
private int n1=100;
public void m1(){
final class Inner02{ //局部内部类
private int n1=800;
public void f1(){
System.out.println("n1="+n1); //输出n1=800(内部类n1)
System.out.println("n1="+Outer02.this.n1); //输出n1=100(外部类n1)
}
}
}
}
注意:
- 局部内部类定义在方法中/代码块
- 作用域在方法体或者代码块中
- 本质仍然是一个类
匿名内部类
匿名内部类是定义在外部类的局部位置,比如在方法中,并且没有类名
- 本质是类
- 内部类
- 该类没有名字
- 同时还是一个对象
基本语法:
1 | new 类或接口(参数列表){ |
匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征。也有创建对象的特征。对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
可以直接访问外部类的所有成员,包含私有的
不能添加访问修饰符,因为它的地位就是一个局部变量
作用域仅仅在定义它的方法或代码块中
匿名内部类—访问—>外部类成员[访问方式:直接访问]
外部其他类—不能访问—>匿名内部类(因为匿名内部类是一个局部变量)
如果外部类和匿名内部类的成员重名时,匿名内部类去访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用(外部类名.this)去访问
成员内部类
可以直接访问外部类所有成员,包括私有的
可以添加任意访问修饰符,因为它的地位就是一个成员
作用域和其它外部类的其它成员一样,为整个类体
外部成员访问成员内部类
1
2
3
4
5
6// 第一种方式 outer08.new Inner08(),相当于把new Inner08()当做是outer08成员
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 第二种方式 在外部类中,编写一个方法,可以返回Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
静态内部类
- 与成员内部类基本相同,只不过增添一个static的前提
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
枚举和注解
枚举介绍
枚举是一组常量的集合
属于一种特殊的类,里面只包含一组有限的特定的对象
自定义类实现枚举
- 构造器私有化,防止直接
new - 不需要提供
setxxx方法,因为枚举对象值通常为只读 - 对枚举对象/属性使用
final+static共同修饰,实现底层优化 - 枚举对象名通常使用全部大写,常量的命名规范
- 枚举对象根据需要,也可以有多个属性
enum关键字实现枚举
- 使用关键字
enum时,会隐式的继承Enum类、不能再extends其他类,但是可以实现接口 - 多个常量用逗号
,隔开 - 要求将定义常量对象写在前面
- 如果使用的是无参构造器,创建常量对象,可以省略
()
enum常用方法
toString():Enum 类已经重写过了Object类的toString 方法,返回的是当前对象名name():返回当前对象名(常量名),子类中不能重写ordinal():返回当前对象的位置号,默认从 0 开始values():返回当前枚举类中所有的对象数组valueOf():将字符串转换成枚举对象,要求传入的字符串必须 为已有的对象名,否则报异常compareTo(Enum e):比较两个枚举常量,比较是位置号

JDK内置的基本注解类型
@Override表示指定重写父类的方法(在编译层面),如果不是则会报错
不写
@Override,但构成方法的重写也不影响如果写了
@Override又没重写就会报错
@Deprecated
@Deprecated修饰某个元素,表示该元素已经过时不推荐使用,但仍然可以使用
从
@Deprecated源码可以看出,可以用来修饰方法,类,包,字段,参数,等等@Deprecated可以做版本升级过渡使用
@SuppressWarnings当我们不希望看到警告的时候,可以用
@SuppressWarnings来抑制警告信息在{“ “}中,可以写入你希望抑制的警告信息
作用范围与
@SuppressWarnings的位置有关
异常
选中代码后ctrl + alt + t
语法错误和逻辑错误不是异常
异常体系图


常见运行时异常
NullPointerException空指针异常当应用程序试图在需要对象的地方使用null时,抛出该异常
ArithmeticException数学运算异常当出现异常的运算条件时,抛出此异常(比如除数为0的除法)
ArrayIndexOutOfBoundsException数组下标越界异常用非法索引访问数组时,抛出的异常
ClassCastException类型转换异常当试图将对象强制转换为不是实例的子类时,抛出此异常
NumberFormatException数字格式不正确异常当试图将字符串转换成一种数值类型,但该字符不能转为适当格式时,抛出异常
常见编译时异常
SQLException操作数据库时,查询表可能发生异常
IOException操作文件时,发生的异常
FileNotFoundException当操作一个不存在的文件时,发生异常
ClassNotFoundException加载类,而该类不存在时,异常
EOFException操作文件,到文件未尾,发生异常
ILLegalArgumentException参数异常
try-catch-finally
1 | try { |
- 可以有多个
catch语句,捕获不同的异常,(进行不同的业务处理),但是要求父类异常在后,子类异常在前 - 可以进行
try-finally配合使用,这种相当于没有捕获异常,因此程序会退出
throws
try-catch-finally和throws二选一
默认是throws

编译异常必须处理
运行时的异常,程序中没有处理,默认就是
throws子类重写的方法,抛出的异常类型要么和父类一致,要么是父类抛出的异常类型的子类型
在
throws过程中,如果由try-catch,就相当于有异常处理,不必throws
自定义异常
- 如果继承
Exception,属于编译异常; - 如果继承
RuntimeException,属于运行异常(一般来说,继承RuntimeException),即把自定义异常做成运行时异常,好处是,我们可以显式的使用默认的处理机制;
1 | public class CustomException { |
throw、throws区别

常用类
包装类


装箱和拆箱
JDK5以前手动装箱和拆箱,JDK5以后自动装箱和拆箱- 自动装箱底层调用的是
valueOf方法,比如Integer.valueOf()
1 | int n1=100; |
1 | int n2=200; |
三元运算符是一个整体,类型转为高一级别的
1 | Object obj1=true? new Integer(1):new Double(2.0); //类型转为高一级别的Double |
1 | Object obj2; |
Integer类
底层缓存了-128~127的值

如果有基本数据类型,判断的则是值是否相同
1 | Integer a=127; |
String类
使用
Unicode字符编码,一个字符(不区分字母还是汉字)占两个字符可以串行化(可以在网络传输),可以比较大小

String是final类,不能被其他的类继承String有属性private final char value[];用于存放字符串内容value是一个final类型,(地址)不可以修改:不能指向新的地址,但单个字符内容可以变化
创建String对象的两种方式
直接赋值
String s = "dzr";先从常量池查看是否有
"dzr"的数据空间,如果有,直接指向;如果没有,则重新创建,然后指向s最终指向的是常量池的空间地址
调用构造器
String s2 = new String("dzr");先在堆中创建空间,里面维护了
value属性,指向常量池的dzr空间如果常量池没有
"dzr",重新创建,如果有,直接通过value指向最终指向的是堆中的空间地址

1 | String a = "dzr"; |
public String intern():最终返回的是常量池的地址(对象)

1 | String a = "hello"; |
String c = a + b的底层:
先创建一个 StringBuilder sb = StringBuilder(),执行sb.append("hello");然后执行sb.append("abc");再执行 String c= sb.toString(),最后c指向堆中的对象(String) value[]指向池中 "helloabc"
总结:
常量相加,看的是池;变量相加,看的是堆
1 | class Test1 { |

常用方法:
equals前面已经讲过了. 比较内容是否相同,区分大小写
equalsIgnoreCase忽略大小写的判断内容是否相等
indexOf获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1
lastIndexOf获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1
substring截取指定范围的子串
trim去前后空格
charAt获取某索引处的字符
toUpperCase转换成大写
toLowerCase转换成小写
concat拼接字符串
replace替换字符串中的字符
split分割字符串
compareTo比较两个字符串的大小,如果前者大,则返回正数,如果相等,返回0
以下内容于2023.3.1开始继续更新
StringBuffer和StringBuilder类
StringBuffer类
基本介绍
可变的字符序列,可以对字符串内容进行增删
很多方法与String相同,但是StringBuffer是可变长度的
String和StringBuffer区别
String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址(传创建新对象),效率较低;//private final char value[];其中字符串常量在常量池中
StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址(除非分配的内存不够了,需要重新分配),效率较高;//char[] value;其中字符串变量在堆中
String 和 StringBuffer 相互转换
1 | public class StringAndStringBuffer { |
StringBuffer 类常见方法
1 | public class StringBufferMethod { |
StringBuilder类
基本介绍
此类被设计用作StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先采取该类,因为在大多数实现总,它比StringBuffer要快
在StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据
String、StringBuffer和StringBuilder的比较
StringBuilder 和StringBuffer 非常相似,均代表可变的字符序列,而且方法也一样;
String:不可变字符序列,效率低,但是复用率高;
StringBuffer:可变字符序列、效率较高(增删)、线程安全(适合多线程);
StringBuilder:可变字符序列、效率最高、线程不安全(适合单线程);
效率:StringBuilder > StringBuffer > String
String、StringBuffer和StringBuilder的选择
如果字符串存在大量的修改操作,一般男使用StringBuffer和StringBuilder
如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
如果字符串存在很少量的修改操作,被多个对象引用,使用String,比如配置信息等
Math类
常见方法
1 | public class MathMethod { |
Date日期类、Calendar日历类以及新的日期
第一代日期类
1 | public class Date01 { |
第二代日期类
Calendar类
1 | public class Calendar_ { |
第三代日期类
LocalDate(年月日)、LocalTime(时分秒)、LocalDateTime(年月日时分秒)
JDK8加入的
LocalDate只包含日期,可以获取日期字段;
LocalTime只包含时间,可以获取时间字段;
LocalDateTime包含日期+时间,可以获取日期和时间字段
DateTimeFormatter 格式日期类
1 | DateTimeFormat dtf = DateTimeFormatter.ofPattern(格式); |
更多的方法
1 | public class LocalDate_ { |
Instant时间戳
1 | public class Instant_ { |
System类
常用方法
exit:退出当前程序
arraycopy:赋值数组元素,比较适合底层调用,一般使用Array.copyOf完成复制数组
1
2
3int[] f1 = {1,2,3};
int[] f2 = new int[3];
System.arraycopy(f1,0,f2,0,3);currentTimeMillens:返回当前时间距离1970-1-1的毫秒数;
gc:运行垃圾回收机制
1 | public class System_ { |
Arrays类
- toString 返回数组的字符串形式
- sort 排序(自然排序和定制排序)
- binarySearch 通过二分搜索法进行查找,要求必须排好序
- copyOf 数组元素的赋值
- fill 数组元素的填充
- equals 比较连个数组元素内容是否完全一致
- asList 将一组值,转换为list
BigInteger类和BigDecimal类
BigInteger类
适合保存比较大的整型
1 | public class BigInteger_ { |
BigDecimal类
适合保存精度更高的浮点型(小数)
1 | public class BigDecimal_ { |
集合
集合的理解
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列方便的操作对象的方法:add,remove,set,get等
- 使用集合添加,删除新元素的代码简洁
1 | public static void main(String[] args) { |
Collection
特点
Collection实现子类可以存放多个元素,每个元素可以是Object
有些Collection的实现类,可以存放重复的元素,有些不可以
有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
Collection接口没有直接的实现子类,是通过它的子接口Set 和 List来实现的
常用方法
1 | public static void main(String[] args) { |
Iterator迭代器遍历
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即可以返回一个迭代器
- Iterator 的结构
- Iterator仅用于遍历集合, Iterator本身不存放对象
1 | public class CollectionIterator { |
for循环加强遍历元素
简化版的迭代器遍历
1 | public class CollectionFor { |
List接口和常用方法
基本介绍
List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
List集合中的每个元素都有其对应的顺序索引(即一个整数型的序号记载其在容器中的文字hi,可以根据序号存取容器中的元素),即支持索引
1 | public class List_ { |
常用方法
void add(int index, Object ele):在index位置插入ele元素,没有index,默认在最后插入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位置的子集合[fromIndex,toIndex)
ArrayList类
注意事项
- ArrayList可以加入null,并且可以有多个
- ArrayList 是由数组来实现数据存储的
- ArrayList 基本等同于Vector,除了ArrayList是线程不安全的(但执行效率高),在多线程情况下,不建议使用ArrayList
底层分析
- ArrayList中维护了一个object类型的数组elementData
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次田间,则扩容elementData为10,如果需要再次扩容,则扩容elementData为1.5倍
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
Vector
基本介绍
- Vector底层是一个对象数组,protected Object[] elementData
- Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
- 开发中,需要线程同步安全时,考虑使用Vector
1 | public class Vector_ { |
Vector和ArrayList的比较

LinkedList底层结构
LinkedList的说明
- LinkedList底层实现了双向链表和双端队列的特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
LinkedList的底层操作机制
LinkedList底层维护了一个双向链表
LinkedList中维护了两个属性first 和 last 分别指向首节点和尾节点
每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率比较高
LinkedList 的增删改查和循环遍历案例
1 |
|
ArrayList 和 LinkedList 比较
ArrayList 和 LinkedList 比较
随机更新
因为看mybatis的时候用到了反射所以来补一补
反射

优点
可以动态的创建和适用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
缺点
使用反射基本是解释执行,对执行速度有影响
反射调用优化
使用setAccessible,禁用访问检查,true为禁用
Class类
常用方法

获取方式
一直一个类的全名,且该类在类路径下,可以通过
Class类的静态方法forName()获取,可能会抛出ClassNotFoundException- Class cls1 = Class.forName(“java.lang.Cat”);
- 多用于配置文件,读取类全路径,加载类
若已知具体的类,通过类的
class获取- Class cls2 = Cat.class;
- 多用于参数传递,比如通过反射得到对应构造器对象
已知某个类的实例,调用该实例的
getClass()方法获取Class对象- Class cls3 = 对象.getClass();//运行类型
- 通过创建好的对象,获取Class对象
其他方式:
ClassLoader cl = 对象.getClass().getClassLoader();- class cls4 = cl.loadClass(“类的全类名”);
基本数据按如下方式得到
Class类对象- Class cls5 = 基本数据类型.class;
基本数据类型可以通过
.TYPE得到Class类对象- Class cls6 = 包装类.TYPE
哪些类型有Class对象
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface
- 数组
- enum
- annotation
- 基本数据类型
- void
类加载
基本说明
- 静态加载
- 编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载
- 运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不报错,降低了依赖性
类加载时机
- 当创建对象时(new)
- 静态加载
- 当子类被加载时,父类也加载
- 静态加载
- 调用类中的静态成员时
- 静态加载
- 通过反射
- 动态加载


反射获取类的结构信息
Class类

Field类

Method类

Constructor类

通过反射创建对象
调用类中的public修饰的无参构造器
调用类中的指定构造器
Class类相关方法
newInstance:调用类中的无参构造器,获取对应类的对象
getConstructor(Class…class):根据参数列表,获取对应的构造器对象
getDecalaredConstructor(Class…class):根据参数列表,获取对应的构造器对象
Constructor类相关方法
- setAccessible:暴破
- newInstance(Object…obj):调用构造器
通过反射访问类中的成员
访问属性
- 根据属性名获取Field对象
- Field f = class对象.getDeclaredField(属性名);
- 暴力破解
- f.setAccessible(true);
- f 是Field
- 访问
- f.set(o,值)
- 如果是静态属性,则set和get中的参数o,可以写成null
访问方法
- 获取对象
- Object o = class.newInstance();
- 暴力破解
- m.setAccessible(true);
- 访问
- Object returnValue = m.invoke(o,实参列表);
- 注意
- 如果是静态方法, 则invoke的参数o,可以写成null
Lambda表达式
格式
1 | (匿名内部类被重写方法的形参列表)->{ |
实例
1 | //加上这个注解必须是函数式接口,且只有一个抽象方法 |
也可以将其作为参数代入方法中去使用
1 | //在原来的基础上定义一个pk方法 |
省略规则
- 参数类型可以省略不写
1 | //精简之前: |
- 如果只有一个参数,参数类型可以省略,同时()也可以省略
1 | //简单引用一个单个参数的例子,不需要了解其具体含义 |
- 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号
1 | //参照上一条规则的例子 |
- 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略”;”不写
1 | //参照第一条规则的代码 |