- 类执行顺序
- 继承
- 方法重写
- 构造器
- 多态
- 抽象类
- 接口
- 枚举类
- 字符串
- 内部类
- 匿名内部类
- 拆箱与装箱
- 克隆
- 浅克隆:
- 深克隆:
- 正则表达式
- Lambda
- 方法引用
- 静态方法引用
- 实例方法引用
- 特定类型方法引用
- 构造器引用
- 泛型
- 通配符
- 集合
- Collection
- 遍历方式
- 迭代器
- foreach
- Lambda
- 并发修改异常
- Set
- 自定义对象去重
- Map
- 遍历
- Stream流
- File
- IO流
类执行顺序
class OrderDemo {// 1. 类加载阶段:静态成员(按顺序)static int staticVar = 10;static {System.out.println("静态代码块:staticVar=" + staticVar); // 输出10staticVar = 20;}// 2. 实例化阶段:实例成员(按顺序)int instanceVar = 1;{System.out.println("实例代码块1:instanceVar=" + instanceVar); // 输出1instanceVar = 2;}int instanceVar2 = instanceVar + 1;{System.out.println("实例代码块2:instanceVar2=" + instanceVar2); // 输出3}// 3. 构造方法(最后执行)public OrderDemo() {System.out.println("构造方法:staticVar=" + staticVar + ",instanceVar=" + instanceVar);// 输出:staticVar=20(类加载时修改后的值),instanceVar=2(实例代码块修改后的值)}public static void main(String[] args) {System.out.println("首次创建实例:");new OrderDemo(); // 触发类加载+首次实例化System.out.println("\n第二次创建实例:");new OrderDemo(); // 仅执行实例化阶段(类加载已完成)}
}
继承
方法重写
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
当通过对象调用方法时,会优先执行该对象实际类型中重写的方法
Parent p1 = new Parent();Parent p2 = new Child(); // 父类引用指向子类实例Child c = new Child();p1.print(); // 执行Parent的print()p2.print(); // 执行Child的print()c.print(); // 执行Child的print()
注意: 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。(why?)
构造器
子类所有构造方法的第一行都会默认先调用父类的无参构造方法
多态
1.子类对象继承的父类类型,或者实现的父接口类型。
2.方法的重写【意义体现:不重写,无意义】
3.父类引用指向子类对象【格式体现】
e.g:
Person.java
public class Person {String name;int age;Person(String name, int age) {this.name = name;this.age = age;}public void show(){System.out.println("Name: " + name + ", Age: " + age);}
}
Man.java
public class Man extends Person {String Sex="Male";Man(String name, int age) {super(name, age);}@Overridepublic void show() {System.out.println("I am man");}
}
Female.java
public class Female extends Person {String Sex="Female";Female(String name, int age) {super(name, age);}@Overridepublic void show() {System.out.println("I am Female");}
}
Main.java
public class Main {public static void main(String[] args) {Person p1 = new Man("John", 30);Person p2 = new Female("lady", 25);p1.show();p2.show();}
}
result:
这便是同一行为,具有多个不同表现形式。
调用特性:
Person P=new man("John", 30);
调用成员变量时:编译看左边,运行看左边
P.Sex;
编译看左边:编译的时候看左边父类中有没有这个变量,没有就报编译错误
运行看左边:运行的时候实际获取的就是父类这个变量的值
结论:对于变量来说子类变量父类不能直接访问,可以通过子类的get/set函数来访问或者向下转型将父类引用转为子类引用后,才能访问子类变量
P.show()
调用成员方法时:编译看左边,运行看右边
编译看左边:编译的时候看左边父类中有没有这个方法,没有就报编译错误
运行看右边:运行的时候实际使用的是子类中重写的方法优先使用
结论:编译器检查变量是否可访问时,只依据引用变量的声明类型(即父类类型),而非运行时的实际对象类型(子类)。实际上运行时能准确指向但是编译时搞不清楚。
抽象类
注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
接口
在JDK7,包括JDK7之前,接口中的只有包含:抽象方法和常量
接口中的抽象方法默认会自动加上public abstract修饰,在接口中定义的成员变量默认会加上: public static final修饰,程序员无需自己手写!!
如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
** 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。**
类与接口是实现关系
接口与接口是继承关系
JDK8以后接口中新增默认方法:public default 返回值类型 方法名(参数列表){}
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
在接口中,被static修饰的方法,不能被重写,可以直接通过接口名调用
如果一个方法中,当参数为接口时,那么在调用方法时就可传递这个接口的所有实现类对象
理解:接口的本质是一份“契约”,它仅定义“必须做什么”(即方法签名),而不规定“具体怎么做”(即方法实现),核心作用是解耦“行为定义”与“行为实现”,实现多模块间的规范统一 。
解耦:接口隔离了“调用者”和“实现者”。调用者只需知道接口定义的方法,无需关心具体是哪个类实现的(如电脑调用 USB 设备,无需管是U盘还是鼠标,只要符合 USB 接口契约就能用),极大提升了代码的灵活性和可扩展性 。
区别:接口只是一个未实现方法的集合,而抽象类语义上有继承关系
思考:如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?
可以在接口跟实现类中间,新建一个中间类(适配器类)
让这个适配器类去实现接口,对接口里面的所有的方法做空重写。
让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。
因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象
枚举类
枚举类本质是特殊类,枚举常量(如 ALIPAY)就是这个类的“预定义实例”——在枚举类加载时,JVM 会自动创建所有枚举常量,每个常量都是枚举类的一个对象,且全局唯一(不能通过 new 再创建) 。
/* 格式
enum 枚举名 {枚举常量1(值),枚举常量2(值),……
} */
enum DayOfWeek {MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);private int value;DayOfWeek(int value) {this.value = value;}public int getValue() {return this.value;}
}
// 使用
DayOfWeek day = DayOfWeek.MONDAY;
int value = day.getValue();
System.out.println("Today is " + day + ", value is " + value);// 输出Today is MONDAY, value is 1
实现接口的枚举常量
/* 格式
public interface Operation {int apply(int x, int y);
} */public enum BasicOperation implements Operation {PLUS("+") {public int apply(int x, int y) { return x + y; }},MINUS("-") {public int apply(int x, int y) { return x - y; }},TIMES("*") {public int apply(int x, int y) { return x * y; }},DIVIDE("/") {public int apply(int x, int y) { return x / y; }};private final String symbol;BasicOperation(String symbol) {this.symbol = symbol;}@Override public String toString() {return symbol;}
}// 使用
int result = BasicOperation.PLUS.apply(1, 2); // 3
匿名内部类
// 例如
enum Operation {PLUS {public int apply(int x, int y) {return x + y;}},MINUS {public int apply(int x, int y) {return x - y;}},TIMES {public int apply(int x, int y) {return x * y;}},DIVIDE {public int apply(int x, int y) {return x / y;}};public abstract int apply(int x, int y);
}// 使用
int result = Operation.PLUS.apply(1, 2) // 返回值为3
字符串
内部类
匿名内部类
{}中才是匿名类,Swim表示实现/继承自它,new表示创建一个实例,()表示调用空参构造器
Swim.java
public abstract class Swim {int a,b;public Swim(int a,int b){this.a=a;this.b=b;}public abstract void swim();
}
Main.java
public class Main {public static void main(String[] args) {new Swim(1,2){@Overridepublic void swim() {System.out.println(a+b);}}.swim();}
}
拆箱与装箱
//1.利用构造方法获取Integer的对象(JDK5以前的方式)
/*Integer i1 = new Integer(1);Integer i2 = new Integer("1");System.out.println(i1);System.out.println(i2);*///2.利用静态方法获取Integer的对象(JDK5以前的方式)
Integer i3 = Integer.valueOf(123);
Integer i4 = Integer.valueOf("123");
Integer i5 = Integer.valueOf("123", 8);System.out.println(i3);
System.out.println(i4);
System.out.println(i5);//3.这两种方式获取对象的区别(掌握)
//底层原理:
//因为在实际开发中,-128~127之间的数据,用的比较多。
//如果每次使用都是new对象,那么太浪费内存了
//所以,提前把这个范围之内的每一个数据都创建好对象
//如果要用到了不会创建新的,而是返回已经创建好的对象。
Integer i6 = Integer.valueOf(127);
Integer i7 = Integer.valueOf(127);
System.out.println(i6 == i7);//trueInteger i8 = Integer.valueOf(128);
Integer i9 = Integer.valueOf(128);
System.out.println(i8 == i9);//false//因为看到了new关键字,在Java中,每一次new都是创建了一个新的对象
//所以下面的两个对象都是new出来,地址值不一样。
/*Integer i10 = new Integer(127);Integer i11 = new Integer(127);System.out.println(i10 == i11);Integer i12 = new Integer(128);Integer i13 = new Integer(128);System.out.println(i12 == i13);*/
克隆
浅克隆:
含义:浅克隆是指在克隆对象时,只复制对象本身和其中的基本类型数据,而不复制对象中引用类型的数据,此时复制出来的对象与原对象共享引用类型数据所在的地址。
特点:当原对象中引用类型数据发生改变时,复制出来的对象也会发生变化。
深克隆:
含义:在克隆对象时,不仅复制对象本身和其中的基本类型数据,还要对对象中的引用类型数据进行递归复制,以确保复制出来的对象与原对象的所有数据完全独立,互不影响
特点:可以保证克隆出来的对象与原对象完全独立,互不影响
注意:深克隆会比浅克隆更加耗费时间和资源,但可以保证克隆出来的对象与原对象完全独立,互不影响。而浅克隆虽然速度快,但是容易出现数据共享的问题,需要特别注意
不会自己写,使用别人的工具类
import com.google.gson.Gson;public class Test {public static void main(String[] args) {Person p1=new Person("Alice", 30);System.out.println(p1);Gson gson=new Gson();//将对象转换为JSON字符串String json=gson.toJson(p1);System.out.println(json);//将JSON字符串转换为对象Person p3=gson.fromJson(json,Person.class);p3.setName("bob");System.out.println(p3);}
}
正则表达式
public class Test {public static void main(String[] args) {String str="sb shisohfs ss";//获得正则表达式对象Pattern p= Pattern.compile("sb");//获取文本匹配器对象Matcher m = p.matcher(str);//find()如果有返回true且底层记录子串起始索引和结束索引+1while(m.find()){//group()返回字串,调用的是subString()System.out.println(m.group());}}
}
//?表示前面的Java,=表示连接后面的8或11或17但是不包含在结果中,(?i)表示后面字段大小写都匹配String reg="((?i)Java)(?=8|11|17)";String reg="((?i)Java)(?:8|11|17)"; //:表示留下在结果中String reg="((?i)Java)(?!8|11|17)";//!表示不要这样的串
贪婪爬取:ab+,尽可能多的b
非贪婪爬取:ab+?,尽可能少的b
正则内部:\组号
正则外部:$组号
(?:)(?=)(?!)是非捕获分组不占用组号
Lambda
方法引用
简化了Lambda
静态方法引用
实例方法引用
特定类型方法引用
构造器引用
泛型
//泛型类
public class Test <E> {public Test() { }public void add(E e){System.out.println(e.toString());}//泛型方法public static <T> T show(T t){return (T) t.toString();}public static void main(String[] args) {Test<Person> t= new Test<>();t.add(new Person());System.out.println(Test.show("adidas"));}
}
通配符
泛型只支持引用类型,因为泛型会被擦除,它只工作在编译阶段,编译后它就没有了,所有类型会变为Object,而Object接不了基本类型。
集合
Collection
遍历方式
迭代器
foreach
Lambda
并发修改异常
就是一边遍历一边增删
foreach与Lambda解决不了并发修改异常问题,只适合做遍历。
Set
自定义排序规则:
1.实现Comparable接口,重写comparaTo方法
2.TreeSet自带比较器对象,指定规则。
自定义对象去重
Map
键不重复,同键的值后面覆盖前面的
遍历
1.根据键找值
HashMap<String, Integer> m = new HashMap<String,Integer >();
Set<String> keys=m.keySet();
for(String key:keys){m.get(key);
}
2.键值对
Map.Entry是接口不是对象,实现类是其内部的Node (key,value)
3.Lambda
Stream流
of(T ... values)中...表示可变参数
Optional是一个含量为1的容器,可以存NULL