Skip to content

面向对象

java
// 类名首字母大写
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

java
String a = "hello"; // 存到串池里
String b = "hello"; // 串池里有了该值,直接复用内存地址
// 所以 a == b 内存地址相同 true

String c = "hello";  // 存串池里
String d = new String("Hello"); // 存到堆里
//  c == d 内存地址不同 false
// 如果要比较值,可以使用 equals 方法

如果每次进行拼接都会产生新的字符串

java
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 一样是个容器,内容可变,但可以指定拼接的格式。

java
public StringJoiner(间隔符号)
public StringJoiner(间隔符号, 开始符号, 结束符号)

常用方法:add、length、toString

字符串原理

  • 字符串存储的内存原理:直接赋值会复用常量池中的;new 出来的不会复用,而是开辟新空间
  • ==号比较的是什么:基本数据类型比较数据值;引用数据类型比较地址
  • 字符串拼接底层原理:如果是 String s = "a" + "b" + "c"; 这种写法,最后编译器会优化为 String s = "abc";; 如果是
java
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

java
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 重写注解

注意事项:

  • 重写方法的名称、形参都要和父类一致
  • 子类重写方法的访问权限必须大于等于父类
  • 子类返回值类型必须小于等于父类
  • 只有能被添加到虚方法表里的方法才能被重写

多态

同类型的对象,表现出不同的形态。

java
// 父类类型 对象名称 = 子类对象
Animal a = new Dog();

// 打印 animal,而不是 dog
a.name;

// 叫
a.woof();
// 最后打印狗叫,而不是左边的动物叫

多态的前提

  • 有继承关系
  • 有父类引用指向子类对象

调用特点

  • 调用成员变量时,编译看左边,运行也看左边
  • 调用成员方法时,编译看左边,运行看右边 比如上面的 Animal 例子

成员变量:在子类的对象中,会把父类的成员变量也继承下来。父:name,子:name 成员方法:如果子类对方法进行了重写,那么在虚方法表里会对父类方法进行覆盖。

弊端

不能使用子类的特有方法,但是可以使用强制类型转换来使用,但不能乱转,否则运行时会报错,增强健壮性可以使用 instanceof 来判断

java
Dog d = (Dog)a;

包就是文件夹,用来管理不同的 Java 类

  • 使用同一个包中的类不需要 import
  • 使用 java.lang 包中的类,不需要 import
  • 其它情况需要 import
  • 如果同时使用两个包中的同名类,需要用全类名

final

表示不可改变,可以用来修饰方法、类、变量

  • 方法:表示这是最终方法,不能被重写
  • 类:表示这是最终类,不能被继承
  • 变量:表示这是常量,只能赋值一次,如果修改的是引用类型,那么地址值不能改变,但是内部的可以改变

权限修饰符

修饰符同类同包中其它类不同包下的子类不同包下的无关类
private
空着
protected
public

静态代码块

java
static {}

随着类的加载而加载,自动触发,且只触发一次,适合在类加载的适合,做一些数据初始化的操作

抽象类

作用:强制让子类按照定义的抽象方法进行重写,增强后期可维护性

抽象方法:将共性的方法抽取到父类,每一个子类执行的内容不一样,所以在父类中不能确定具体的方法体。 抽象类:如果一个类中存在抽象方法,那么该类必须声明为抽象类

java
// 抽象类
public abstract class Test {}

// 抽象方法
public abstract void test() {}
  • 抽象类不能实例化
  • 抽象类不一定有抽象方法,有抽象方法的一定是抽象类
  • 可以有构造方法
  • 抽象类的子类:要么重写抽象类中的所有抽象方法、要么是抽象类

接口

  • 接口不能实例化
  • 接口和类之间是实现关系,使用 implements 关键字
  • 接口可以单实现,也可以多实现,还可以在继承一个类的同时进行多实现
java
public class Demo extends Parent implements Interface1, Interface2 {}

接口中成员的特点

  • 成员变量:只能是常量,默认修饰符 public static final
  • 构造方法:没有
  • 成员方法:只能是抽象方法,public abstract

类和类之间不能多继承,但是接口和接口之间可以多继承

JDK8 之后允许在接口中定义默认方法,需要使用 default 修饰

java
public default void show() { }

还允许在接口定义静态方法,使用 static 修饰

JDK9 中允许定义私有方法

注意事项:

  • 默认方法不是抽象方法,不强制重写,但如果被重写,重写的时候就需要去掉 default 关键字
  • public 可以省略,default 不能省略
  • 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写

适配器设计模式

  • 当一个接口中抽象方法过多,但我只需要使用其中的一部分方法,就可以使用适配器模式
  • 先编写中间抽象类: XXXAdapter,实现对应的接口,对接口中的抽象方法进行空实现,让真正的类去继承这个中间抽象类,并重写需要用的方法

内部类

类的五大成员:属性、方法、构造方法、代码块、内部类。 在类的内部再定义一个类称为内部类 什么时候用内部类:B 类表示的事物是 A 类的一部分,且 B 单独出现没有意义,比如车的发动机

java
public class Outer {
    public class Inner {}
}

main() {
  Outer.Inner oi = new Outer().new Inner();
}

如果内部类是私有的,则在外部类提供一个 getInstance 方法获取内部类 当外部类成员和内部类成员变量重名时,内部类如何访问:Outer.this.变量名

静态内部类

静态内部类只能访问外部类中的静态变量和静态方法, 如果需要访问非静态的需要创建对象

java
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();
}

局部内部类(没啥用)

将内部类定义在方法里,外界无法调用,需要在方法体内部进行实例化使用,该类可以访问外部类的成员,也可以访问方法内的局部变量

匿名内部类

隐藏了名字的内部类 作用是:继承/实现、方法重写、创建对象

java
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();
}

使用场景:当方法的参数是接口或类时,以接口为例,可以传递这个接口的实现类对象

每天进步一丢丢