面向对象
// 类名首字母大写
public class 类名 {
1. 成员变量
String name;
int age;
2. 成员方法
public void call() {
// 打电话
}
3. 构造器(构造函数)
// 给成员遍历初始化用的, 如果没定义,系统会自动创建无参构造方法,定义了则不再创建
修饰符 类名(参数) {
// 方法体
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
4. 代码块
5. 内部类
}
用来描述一类事物的类,专业叫做:Javabean 类,里面不写 main 方法; 编写 main 方法的类叫做测试类,可以在测试类中创建 Javabean 类的实例并进行测试。
标准 JavaBean
- 类名见名知意
- 成员变量使用 private
- 提供至少两个构造方法(无参和有参的)
- 成员方法(每一个成员变量提供 getter 和 setter)
可以使用 ide 的 PTG 插件快速生成
String
String a = "hello"; // 存到串池里
String b = "hello"; // 串池里有了该值,直接复用内存地址
// 所以 a == b 内存地址相同 true
String c = "hello"; // 存串池里
String d = new String("Hello"); // 存到堆里
// c == d 内存地址不同 false
// 如果要比较值,可以使用 equals 方法
如果每次进行拼接都会产生新的字符串
String s1 = "1";
String s2 = "2";
String s3 = "3";
// s1 + s2 会创建一个新的字符串, (s1+s2) + s3 又会创建一个新的,效率低
String s4 = s1 + s2 + s3;
StringBuilder
可以看成一个容器,创建完字符串后里面的内容是可变的,提高字符串的操作效率 常用方法:append、reverse、length、toString(转成普通的 String)
StringJoiner
拼接速度快,代码比上面的会更简单,和 Builder 一样是个容器,内容可变,但可以指定拼接的格式。
public StringJoiner(间隔符号)
public StringJoiner(间隔符号, 开始符号, 结束符号)
常用方法:add、length、toString
字符串原理
- 字符串存储的内存原理:直接赋值会复用常量池中的;new 出来的不会复用,而是开辟新空间
- ==号比较的是什么:基本数据类型比较数据值;引用数据类型比较地址
- 字符串拼接底层原理:如果是
String s = "a" + "b" + "c";
这种写法,最后编译器会优化为String s = "abc";
; 如果是
String a = "a";
String b = a + "b";
String c = b + "c";
上面 b 和 c 一共创建了两个 StringBuilder 对象
如 String b = a + "b";
最后会变成下面这样: 这种写法,最后会相当于调用 StringBuilder new StringBuilder().append(a).append("b").toString()
上面是 JDK8 以前的拼接方式,在 JDK8 以后的拼接方式是编译的时候也要提前去预估拼接后的总大小,把要拼接的内容放到数组中,最后返回一个新的对象。 结论:如果很多字符串变量拼接,不要直接用 +,否则底层会创建多个对象,浪费性能。
- StringBuilder 只会创建一个对象,把要拼接的内容都往里塞,从而节约内存。一开始会创建一个字节数组,默认容量为 16,扩容时会容量 x2 + 2,如果扩容后长度还不够则以实际长度为准
集合
ArrayList
ArrayList<String> list = new ArrayList<>();
// 基础数据类型需要使用包装类的泛型
ArrayList<Integer> list = new ArrayList<>();
常用方法:add、remove、set、get、size
static 静态变量
使用 static 时堆内存会开辟一块静态区专门存放
被 static 修饰的成员变量叫做静态变量,可以被该类所有对象共享。使用类名来调用/对象名调用
被 static 修饰的成员方法叫做静态方法,一般是测试类/工具类来使用,比如 System(System.out.println) 就是一个工具类,工具类的构造函数使用 private,不让外界进行创建,里面的方法都是静态方法。
总结:静态方法中只能访问静态,非静态方法可以访问所有,静态方法没有 this 关键字
main 方法
这个方法会被虚拟机进行调用,一个通用的 main 方法名,虚拟机才能识别到 public 权限足够大,void 不需要返回值,static 是一个静态方法,由虚拟机直接调用, String[] args 可以接收额外的参数,现在一般用不到,为了兼容以前老代码的。
继承
面向对象三大特征:封装、继承、多态 只支持单继承、不支持多继承、但支持多层继承,一个子类只能继承一个父类。Java 中所有的类都直接或间接继承于 Object 类。
父类构造方法不能被继承。
方法重写
当父类中的方法不能满足子类的需求时,可以进行重写,当子类出现了和父类一模一样的方法声明,就称子类这个方法是重写的方法。使用 @Override 重写注解
注意事项:
- 重写方法的名称、形参都要和父类一致
- 子类重写方法的访问权限必须大于等于父类
- 子类返回值类型必须小于等于父类
- 只有能被添加到虚方法表里的方法才能被重写
多态
同类型的对象,表现出不同的形态。
// 父类类型 对象名称 = 子类对象
Animal a = new Dog();
// 打印 animal,而不是 dog
a.name;
// 叫
a.woof();
// 最后打印狗叫,而不是左边的动物叫
多态的前提
- 有继承关系
- 有父类引用指向子类对象
调用特点
- 调用成员变量时,编译看左边,运行也看左边
- 调用成员方法时,编译看左边,运行看右边 比如上面的 Animal 例子
成员变量:在子类的对象中,会把父类的成员变量也继承下来。父:name,子:name 成员方法:如果子类对方法进行了重写,那么在虚方法表里会对父类方法进行覆盖。
弊端
不能使用子类的特有方法,但是可以使用强制类型转换来使用,但不能乱转,否则运行时会报错,增强健壮性可以使用 instanceof 来判断
Dog d = (Dog)a;
包
包就是文件夹,用来管理不同的 Java 类
- 使用同一个包中的类不需要 import
- 使用 java.lang 包中的类,不需要 import
- 其它情况需要 import
- 如果同时使用两个包中的同名类,需要用全类名
final
表示不可改变,可以用来修饰方法、类、变量
- 方法:表示这是最终方法,不能被重写
- 类:表示这是最终类,不能被继承
- 变量:表示这是常量,只能赋值一次,如果修改的是引用类型,那么地址值不能改变,但是内部的可以改变
权限修饰符
修饰符 | 同类 | 同包中其它类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | √ | |||
空着 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
静态代码块
static {}
随着类的加载而加载,自动触发,且只触发一次,适合在类加载的适合,做一些数据初始化的操作
抽象类
作用:强制让子类按照定义的抽象方法进行重写,增强后期可维护性
抽象方法:将共性的方法抽取到父类,每一个子类执行的内容不一样,所以在父类中不能确定具体的方法体。 抽象类:如果一个类中存在抽象方法,那么该类必须声明为抽象类
// 抽象类
public abstract class Test {}
// 抽象方法
public abstract void test() {}
- 抽象类不能实例化
- 抽象类不一定有抽象方法,有抽象方法的一定是抽象类
- 可以有构造方法
- 抽象类的子类:要么重写抽象类中的所有抽象方法、要么是抽象类
接口
- 接口不能实例化
- 接口和类之间是实现关系,使用 implements 关键字
- 接口可以单实现,也可以多实现,还可以在继承一个类的同时进行多实现
public class Demo extends Parent implements Interface1, Interface2 {}
接口中成员的特点
- 成员变量:只能是常量,默认修饰符 public static final
- 构造方法:没有
- 成员方法:只能是抽象方法,public abstract
类和类之间不能多继承,但是接口和接口之间可以多继承
JDK8 之后允许在接口中定义默认方法,需要使用 default 修饰
public default void show() { }
还允许在接口定义静态方法,使用 static 修饰
JDK9 中允许定义私有方法
注意事项:
- 默认方法不是抽象方法,不强制重写,但如果被重写,重写的时候就需要去掉 default 关键字
- public 可以省略,default 不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
适配器设计模式
- 当一个接口中抽象方法过多,但我只需要使用其中的一部分方法,就可以使用适配器模式
- 先编写中间抽象类: XXXAdapter,实现对应的接口,对接口中的抽象方法进行空实现,让真正的类去继承这个中间抽象类,并重写需要用的方法
内部类
类的五大成员:属性、方法、构造方法、代码块、内部类。 在类的内部再定义一个类称为内部类 什么时候用内部类:B 类表示的事物是 A 类的一部分,且 B 单独出现没有意义,比如车的发动机
public class Outer {
public class Inner {}
}
main() {
Outer.Inner oi = new Outer().new Inner();
}
如果内部类是私有的,则在外部类提供一个 getInstance 方法获取内部类 当外部类成员和内部类成员变量重名时,内部类如何访问:Outer.this.变量名
静态内部类
静态内部类只能访问外部类中的静态变量和静态方法, 如果需要访问非静态的需要创建对象
public class Car {
String name;
String color;
static class Engine {
String name;
String color;
public void show1() {}
public static void show2() {}
}
}
main() {
// 调用 show1 非静态方法
Outer.Inner oi = new Outer.Inner();
oi.show1();
// 调用 show2 静态方法
Outer.Inner.show2();
}
局部内部类(没啥用)
将内部类定义在方法里,外界无法调用,需要在方法体内部进行实例化使用,该类可以访问外部类的成员,也可以访问方法内的局部变量
匿名内部类
隐藏了名字的内部类 作用是:继承/实现、方法重写、创建对象
public interface Swim {
public abstract void swim();
}
main() {
// 匿名内部类
new Swim() {
@Override
public void swim() {
System.out.println("重写 swim 方法");
}
}
// 也可以赋予变量
Swim s = new Swim() {
@Override
public void swim() {
System.out.println("重写 swim 方法");
}
}
// 或者直接链式调用
new Swim() {
@Override
public void swim() {
System.out.println("重写 swim 方法");
}
}.swim();
}
使用场景:当方法的参数是接口或类时,以接口为例,可以传递这个接口的实现类对象