【Java】类、方法、对象、重载重写、构造器详解 面向对象基本知识整理

1.类和对象的区别

  1. 类是抽象的,概念的,代表一类事物,是自定义的数据类型。
  2. 对象是具体的,实际的,代表一个具体事物,是实例。
  3. 类是对象的模板,对象是类的一个个体,对应一个实例。

2.对象在内存中的存在形式

image-20220525163123213

真正的对象是堆中的数据,栈中的cat只是对象引用,对象名称。

3.类和对象的内存分配机制

栈:一般存放基本数据类型(局部变量)

堆:存放对象(Cat cat、数组)

方法区:常量池(常量、字符串),类加载信息

4.Java常见对象流程分析

  1. 先加载类信息,只会加载一次
  2. 在堆中分配信息,进行初始化
  3. 把地址赋给对象引用,对象引用指向对象
  4. 初始化属性,字符串需要在常量池中创建
注意:
Person a = new Person();
a.age 假设存在age int类型 可以输出 无初始化为0
a = null;
a.age 不存在 出现异常 a直接无指向 不是空Person

5.方法调用机制

6.方法好处

  1. 提高代码复用性
  2. 实现细节封装,供用户调用,不用关注方法如何实现

7.方法使用细节

同一个类直接调用方法

引用数据类型传递的是地址,可以通过形参影响实参。

基本数据类型是值传递。

方法中对引用形参修改,可能影响实参内容,不影响实参指向

8.递归

  1. 执行一次方法,就创建一个新的受保护的独立空间(栈空间)
  2. 方法局部变量式独立的,不会相互影响
  3. 方法中是引用类型变量,共享数据
  4. 必须有退出条件
  5. 递归必须向退出条件逼近
public class Fibonacci {

    public static void main(String[] args) {
        System.out.println("factorial = " + factorial(5));
        System.out.println("fibonacciNum = " + fibonacciNum(7));
        System.out.println("monkeyEatingPeachOne = " + monkeyEatingPeachOne(10,1));
        System.out.println("monkeyEatingPeachEve = " + monkeyEatingPeachEve(8));
    }

    //阶乘
    public static int factorial(int n){
        if (n == 1){
            return 1;
        }else {
            return factorial(n-1) * n;
        }
    }

    //斐波那契
    public static int fibonacciNum(int n){
        if (n == 1 || n == 2){
            return 1;
        }else {
            return fibonacciNum(n-1) + fibonacciNum(n - 2);
        }
    }

    //猴吃桃 一半加一 第十天剩n个 求第一天
    public static int monkeyEatingPeachOne(int day, int n){
        if (day == 1) {
            return n;
        }else {
            return monkeyEatingPeachOne(day - 1 , (n + 1) *2);
        }
    }

    //猴吃桃 一半加一 第十天剩一个 求第N天
    public static int monkeyEatingPeachEve(int day){
        if (day == 10) {
            return 1;
        }else {
            return (monkeyEatingPeachEve(day+1)+1)*2;
        }
    }
}

//老鼠迷宫
public class MiGong {
    public static void main(String[] args) {
        //0未走 1墙 2路 3死路
        int[][] arr = new int[7][7];
        //画墙
        for (int i = 0 ; i < 7; i++ ){
            arr[0][i] = 1;
            arr[6][i] = 1;
        }
        for (int i = 1 ; i < 6; i++ ){
            arr[i][0] = 1;
            arr[i][6] = 1;
        }

        arr[3][1] = 1;
        arr[2][2] = 1;
        arr[2][3] = 1;

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[0].length; j++) {
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }

        System.out.println("========老鼠出发========");
        miGong(arr,1,1);

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[0].length; j++) {
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }

    }

    public static boolean miGong(int[][] arr, int x, int y){
        if (arr[5][5] == 2){
            return true;
        }else {
            if (arr[x][y] == 0){
                //假设走通
                arr[x][y] = 2;
                //移动策略 下右上左
                if (miGong(arr,x+1,y)){
                    return true;
                }else if (miGong(arr,x,y+1)){
                    return true;
                }else if (miGong(arr,x-1,y)){
                    return true;
                }else if (miGong(arr,x,y-1)){
                    return true;
                }else {
                    arr[x][y] = 3;
                    return false;
                }
            }else {
                return false;
            }
        }
    }

}
//汉诺塔
public class Hannuota {
    public static void main(String[] args) {
        hannuota(3,'1','2','3');
    }

    public static void hannuota(int n,char a,char b, char c){
        if (n == 1){
            System.out.println(a+"->"+c);
        }else {
            //1.先把除了最下面的全放到b
            hannuota(n-1,a,c,b);
            //2.最下面的到c
            System.out.println(a+"->"+c);
            //3.b到c
            hannuota(n-1,b,a,c);
        }
    }
}

9.方法重载

  • 方法名必须相同
  • 形参列表必须不同,形参类型不是名字
  • 返回值类型无要求

10.可变参数

  • …接受可变参数 0-多
  • 接受的可变参数可以当做数组使用
  • 可以直接传递数组
  • 可变参数和普通参数可一起使用,但可变参数必须在最后
  • 形参列表中只能出现一个可变参数
public int sum(int... nums){
    //int 类型的可变参数
}
public int sum(int i, int... nums){
    //int...在最后
}
public int sum(int... i1, int... i2){
    //错误
}

11.作用域

  • 主要变量就是属性(成员变量)和局部变量
  • 局部变量一般是成员方法中定义的变量
  • 作用域分为属性,局部变量
  • 属性可以不赋值,直接使用,因为有默认值
  • 局部变量必须赋值才能使用,因为没有默认值
  • 属性和局部变量可以重名,访问时遵循就近原则
  • 在同一作用域中,同一方法,局部变量不能重名
  • 属性生命周期长,伴随对象创建而创建,伴随对象的销毁而销毁。
  • 局部变量生命周期较短,伴随代码块的执行而创建,伴随代码的结束而销毁。
  • 属性可以本类使用,或其他类使用,对象调用
  • 局部变量只能本类对应方法使用
  • 属性可以加修饰符
  • 局部变量不可以加修饰符

12.构造器

构造器完成新对象的初始化。构造器初始化对象不是创建对象。

如果一个类没有构造器,就会提供一个默认的无参数构造器,这个构造器将所有的实例字段设置为默认值。

如果类中提供了至少一个构造器,但是没有提供无参数的构造器,那么构造对象时如果不提供参数就是不合法的。说明构造器可以重载。

  • 构造器的修饰符可以默认
  • 构造器没有返回值
  • 方法名和类名一致
  • 参数列表和成员方法一致
  • 构造器的调用由系统完成
class Text{
    int a;
    int b;
    int c;
    Text(int a, int b){
        this.a = a;
        this.b = b;
    }
    Text(int a, int b, int c){
        this(a,b);
        this.c = c;
    }
}

13.对象创建流程分析

  1. 加载类信息(.class),只会加载一次
  2. 在堆中分配空间
  3. 完成对象初始化,先是默认初始化,然后显式初始化,最后构造器初始化
  4. 对象堆中的地址返回给栈中引用对象

14.this

java虚拟机会给每个对象分配this,代表当前对象。

简单说,哪个对象调用,this代表哪个对象。

  • this关键字可以用来访问本类的属性,方法,构造器

  • this用于区分当前类的属性和局部变量

  • 访问成员方法的语法:this.方法名

  • 访问构造器语法:this(参数列表)注意只能在构造器使用,且只能在构造器中访问另一个构造器,必须在第一条语句

  • this不能在类定义的外部使用,只能在类定义的方法中使用

15.一些练习题

import java.util.Random;
import java.util.zip.Adler32;

public class Homework {

    public static void main(String[] args) {

        System.out.println(new A01().max(1.2,2.2,3,13));

        String[] arrs = new String[]{"1","a","c"};
        System.out.println(new A02().find(arrs,"a"));

        Book book = new Book();
        book.price = 190;
        book.updatePrice();
        System.out.println(book.price);

        int[] ints = {1, 2, 3, 4, 5};
        int[] ints1 = new A03().copyArr(ints);
        int[] ints2 = new A03().copyArrNew(ints);
        System.out.println(ints.hashCode());
        System.out.println(ints1.hashCode());
        System.out.println(ints2.hashCode());

        Text text = new Text(1,2,3);

        Tom tom = new Tom();
        int[] cais = {0,0,0};
        tom.cai(cais);

    }
    
}

//定义max方法,求double数组最大值
class A01{
    public double max(double... nums){
        double num = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (num < nums[i]){
                num = nums[i];
            }
        }
        return num;
    }
}

class A02{
    public int find(String[] arrs,String s){
        for (int i = 0; i < arrs.length; i++) {
            if (s.equals(arrs[i])){
                return i;
            }
        }
        return -1;
    }
}

class Book{
    int price;
    public void updatePrice(){
        if (this.price > 150){
            this.price = 150;
        }else if (this.price > 100){
            this.price = 100;
        }
    }
}

class A03{
    public int[] copyArr(int[] arrs){
        int[] copyarrs = new int[arrs.length];
        copyarrs = arrs;
        return copyarrs;
    }

    public int[] copyArrNew(int[] arrs){
        int[] copyarrs = new int[arrs.length];
        for (int i = 0; i < copyarrs.length; i++) {
            copyarrs[i] = arrs[i];
        }
        return copyarrs;
    }
}

class Text{
    int a;
    int b;
    int c;
    Text(int a, int b){
        this.a = a;
        this.b = b;
        System.out.println(""+a+b+c);
    }
    Text(int a, int b, int c){
        this(a,b);
        this.c = c;
        System.out.println(""+a+b+c);
    }
}

class Tom{
    int hand;
    public void cai(int... a){

        System.out.println("Tom\t玩家\t结果");
        Random random = new Random();
        int win = 0;

        for (int i = 0; i < a.length; i++) {
            hand = random.nextInt(3);
            if (a[i] == 0){
                if (hand == 0) System.out.println("石头\t石头\t平局");
                else if (hand == 1) {
                    System.out.println("剪刀\t石头\t赢了");
                    win++;
                }
                else System.out.println("布\t石头\t输了");
            }else if (a[i] == 1){
                if (hand == 0) System.out.println("石头\t剪刀\t输了");
                else if (hand == 1) System.out.println("剪刀\t剪刀\t平局");
                else {
                    System.out.println("布\t剪刀\t赢了");
                    win++;
                }
            }else if (a[i]== 2){
                if (hand == 0) {
                    System.out.println("石头\t布\t赢了");
                    win++;
                }
                else if (hand == 1) System.out.println("剪刀\t布\t输了");
                else System.out.println("布\t布\t平局");
            }else {
                System.out.println("输入错误");
            }
        }
        System.out.println("赢了"+win+"次");

    }
}

16.包

  • 区分相同名字的类
  • 类多时,便于管理
  • 控制访问范围

包的本质:实际就是创建不同的文件夹/目录来保存文件

命名规则

  • 只能包含数字、字母、下划线、小圆点
  • 不能用数字开头,不能是关键字或保留字

命名规范:

  • 一般是小写字母和小圆点
  • com.公司名.项目名.业务模块名

注意事项

  • package作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一个package
  • import指令位置在package下,可以多句顺序无要求

17.访问修饰符

控制方法和属性的访问权限

  1. 公开 publi 对外公开
  2. 受保护 protected 对子类和同一个包中的类公开
  3. 默认 没有修饰符 向同一个包的类公开
  4. 私有 private 只有类本身可以访问 不对外公开
访问级别访问控制修饰符同类同包子类不同包
公开public
受保护protected×
默认没有修饰符××
私有private×××

18.封装

理解好处

  1. 隐藏实现细节
  2. 对数据进行验证,保证安全合理

如有特殊需求,而原始构造器无法满足需求,可以在构造器中调用set方法。

19.继承

提高代码复用性

多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

  1. 子类会自动拥有父类定义的属性和方法。
  2. 父类又叫超类,基类。
  3. 子类又叫派生类。

子类必须调用父类构造器,父类构造器先被调用(构造器中默认有super()😉

父类没有无参构造器,则需在子类指定super构造器

super在使用时必须在构造器第一行,所以this和super不能共存(this也只能在第一行)

Object是所有类的超类,所以创建对象引用时会从最上层(Object)一直无参构造至对象引用

IDEA中选中类名ctrl+H查看继承关系

Java中只能继承一个类,单继承机制

子类父类满足is-a关系,有关系再继承,无关系继承毛

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9HOPH0tu-1654265714673)(C:\Users\shiyuhang\AppData\Roaming\Typora\typora-user-images\image-20220530225224693.png)]

获取属性和方法,从子类层层向上逐个查找,就近原则

20.super

访问父类属性/方法,但不能访问父类的private属性/方法

访问父类构造器只能在构造器第一句只能出现一句super(参数列表);

区别点thissuper
访问属性访问本类中的属性,如果本类没有此属性则从父类中继续查找从父类开始查找属性
调用方法访问本类中的方法,如果本类没有此方法则从父类继续查找从父类开始查找方法
调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须放在构造器的首行
特殊表示当前对象子类中访问父类对象

21.重写

方法重写也叫方法覆盖,需满足一下几点

  1. 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
  3. 子类方法不能缩小父类方法的访问权限
名称发生范围方法名形参列表返回类型修饰符
重载本类必须一样类型个数或者顺序至少一个不同无要求无要求
重写父子类必须一样相同子类重写的方法,返回的类型和父类一样或者是父类返回类型的子类子类方法不能缩小父类方法的访问范围

22.多态

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承的基础上的。

多态的具体体现

  1. 一个对象的编译类型和运行类型可以不一致
  2. 编译类型在定义对象时,就确定了,不能改变
  3. 运行类型可以改变
  4. =左是编译类型,=右是运行类型
Animal animal = new Dog();// animal编译类型是Animal,运行类型是Dog
animal = new Cat();// 可以改变animal的运行类型

多态的前提是:两个对象存在继承关系

多态的向上转型

  1. 本质:父类的引用指向了子类的对象
  2. 语法:父类类型 引用名 = new 子类类型();
  3. 特点:
    1. 可以调用父类的成员(遵守访问权限)
    2. 不能调用子类的特有成员(先通过编译最后才java)
    3. 最终运行方法看子类实现

多态的向下转型

  1. 语法:子类类型 引用名 = (子类类型) 父类引用
  2. 只能强转父类的引用,不能强转父类的对象
  3. 要求父类的引用必须是指向new的子类类型
  4. 向下转型后可以调用子类类型中的所有成员
package polymorphism;

public class Test {
    public static void main(String[] args) {
        Animal animal = new Cat("huamao");
        System.out.println(animal);//Cat{name='huamao'}
        // animal.eat();//不能访问

        Cat cat = (Cat) animal;
        // Dog dog = (Dog) animal;// polymorphism.Cat cannot be cast to polymorphism.Dog

        System.out.println(cat);//Cat{name='huamao'}
        cat.eat();//chiyu
        // System.out.println(dog);
    }
}

class Animal{
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Cat extends Animal{

    public Cat(String name) {
        super(name);
    }

    public void eat(){
        System.out.println("chiyu");
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Dog extends Animal{

    public Dog(String name) {
        super(name);
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
}

属性没有重写,属性的值看编译类型,方法看运行类型。

package polymorphism;

public class Test {
    public static void main(String[] args) {

        Animal animal1 = new Cat("hua");
        System.out.println(animal1.age);//10
        System.out.println(animal1);//Cat{name='hua'}

    }
}

class Animal{
    String name;
    int age = 10;

    public Animal(String name) {
        this.name = name;
    }


    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Cat extends Animal{
    int age = 1
            ;
    public Cat(String name) {
        super(name);
    }

    public void eat(){
        System.out.println("chiyu");
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

instanceOf比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

多态的应用:

  1. 多态数组,数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
  2. 多态参数,形参使用父类类型,实参可以为子类(向上转型)

22.1.动态绑定机制

  1. 调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用

正常调用,方法重写

package Dynamic;

public class DyTest {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());//40
        System.out.println(a.sum2());//30

    }
}

class A {
    public int i = 10;

    public int sum() {
        return getI() + 10;
    }

    public int sum2() {
        return i + 20;
    }

    public int getI() {
        return i;
    }
}

class B extends A {
    public int i = 20;

    public int sum() {
        return i + 20;
    }

    public int sum2() {
        return i + 10;
    }

    public int getI() {
        return i;
    }
}

动态绑定

package Dynamic;

public class DyTest2 {
    public static void main(String[] args) {
        A2 a2 = new B2();
        System.out.println(a2.sum());//30
        System.out.println(a2.sum2());//30

    }
}

class A2 {
    public int i = 10;

    public int sum() {
        return getI() + 10;// getI使用的是运行类型getI
    }

    public int sum2() {
        return i + 20;//i直接使用当前i=10
    }

    public int getI() {
        return i;
    }
}

class B2 extends A2 {
    public int i = 20;

    // public int sum() {
    //     return i + 20;
    // }

    // public int sum2() {
    //     return i + 10;
    // }

    public int getI() {
        return i;
    }
}

23.Object类

23.1.equals方法

  1. == 既可以判断基本类型,又可以判断引用类型
  2. == 如果判断基本类型,判断的是值是否相等
  3. == 如果判断引用类型,判断的是地址是否相等,即使不是同一对象
  4. equals:是Object类中的方法,只能判断引用类型。
  5. equals:默认是判断地址相同,子类往往重写,用于判断内容先相同(Integer、String)。

23.2.hashCode方法

返回该对象的哈希码值,提高哈希表性能。

  1. 提高哈希结构容器的效率
  2. 两个引用,如果指向同一个对象,则哈希值肯定一样
  3. 两个引用,如果指向的不同对象,则哈希值不同
  4. 哈希值主要是根据地址得到,但不能完全等价于地址

23.3.toString方法

默认返回:全类名+@+哈希值十六进制

  1. 子类往往重写toString方法,用于返回属性值
  2. 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString
  3. 当直接输出对象时,toString方法会默认的调用

23.4.finalize方法

  1. 对象被回收时,系统自动调用finalize方法,子类可以重写,做一些释放资源操作
  2. 当对象无引用,jvm就认为这个对象时一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁前会先调用finalize方法
  3. 垃圾回收机制调用,是由系统来决定的(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制,运行时不阻塞代码

24.类变量类方法

24.1.类变量

static变量和jdk版本有关,7之后静态域存储于定义类型的Class对象中,Class对象如同堆中其他对象一样,存在于GC堆中。

  1. static变量是同一个类所有对象的共享变量,所以修改和使用是对同一变量的使用
  2. static类变量,在类加载的时候就生成了,就算没有初始化,类一旦加载便可以使用
  3. static类变量遵循访问修饰符
  4. 生命周期是随类加载开始,类消亡而消亡

访问:

  • 类名.类变量名(推荐)/对象.类变量名
  • 非静态变量不可以类名.类变量名

24.2.类方法

当方法不涉及使用对象,可以设计为类方法,提高开发效率。

例如工具类:Math、Arrays、Collections等

  1. 类方法和普通方法都是随类加载而加载,把结构信息存储来方法区
  2. 类方法中无this、super参数,普通有this、super
  3. 类方法中只能使用类变量和类方法,普通都可

访问:

  • 类名.类方法名(推荐)/对象.类方法名
  • 非静态方法不可以类名.类方法名

25.代码块

  1. 抽取相同的代码,减少冗余
  2. 创建对象优先调用代码块
    1. 静态代码块随着类加载而加载只会调用一次
    2. 普通代码块,每创建一次对象则会调用一次
    3. 子类调用,父类也会加载
  3. 调用静态方法和属性时,不会调用普通代码块,会调用静态代码块
  4. 代码块调用优先于构造器

创建对象时:

  1. 从父类开始初始化静态方法静态属性
    • 如果创建了对象,从该对象的父类开始逐步执行普通方法和构造器
  2. 从父类开始,将父类中所有普通代码块和普通属性的初始化和构造器调用结束
    • 如果创建了对象,从该对象的父类开始逐步执行普通方法和构造器
package Dmk;

public class Text {
    public static void main(String[] args) {
        new C();
        /*
        a1()
        aaa111
        hhh
        a1()
        a1()
        aaa111
        hhh
        bbb
        a()
        a1()
        aaa111
        hhh
        a1()
        bhah
        aaa
        a1()
        aaa111
        hhh
        a1()
        a1()
        aaa111
        hhh
        bbb
        a()
        a1()
        aaa111
        hhh
        a1()
         */
    }
}

class A{
    static public int a = new A().a1();
    static public int b = new B().hah();
    static public int c;
    int d = a1();
    static {
        System.out.println("aaa");
    }
    {
        System.out.println("aaa111");
    }
    static public void a(){
        System.out.println("a()");
    }

    public int a1(){
        System.out.println("a1()");
        return 1;
    }

    public A() {
        System.out.println("hhh");
    }
}

class B extends A{
    {
        System.out.println("bbb");
        A.a();
        new A().a1();
    }
    int hah(){
        System.out.println("bhah");
        return 1;
    }
}
class C extends B{
    static public int a = 1;
    static {
        new A().a1();
    }
}

26.单例设计模式

单例设计,所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

单例模式有两种类型,饿汉式,懒汉式

饿汉式(类加载对象就存在了,饿汉着急呀)

package Dl;

public class Eh {
    public static void main(String[] args) {
        // Girl girl = new Girl();//构造器私有,不能new
        Girl instance = Girl.getInstance();
        System.out.println(instance);
    }
}

class Girl{
    private String name;

    //在静态方法中能返回g对象,修饰static
    private static Girl g = new Girl("ch");

    /*
        保障只有一个对象
        1. 构造器私有
        2. 类内部直接创建
        3. 提供static方法直接返回对象
     */
    private Girl(String name) {
        this.name = name;
    }

    public static Girl getInstance(){
        return g;
    }

    @Override
    public String toString() {
        return "Girl{" +
                "name='" + name + '\'' +
                '}';
    }
}

懒汉式(使用时在创建对象)

观察以上代码,可以发现,我们如果要在使用时创建对象,则可以不在静态Girl中使用new,而是在getInstance()方法调用时赋给staticGirl对象,这样保证了一个Girl和使用时调用。

package Dl;

public class Lh {
    public static void main(String[] args) {
        // Girl2 girl = new Girl2();//构造器私有,不能new
        Girl2 instance = Girl2.getInstance();
        System.out.println(instance);
    }
}

class Girl2 {
    private String name;

    public static int a = 1;
    //在静态方法中能返回g对象,修饰static
    private static Girl2 g;

    /*
        保障只有一个对象
        1. 构造器私有
        2. 类内部定义对象默认null
        3. 提供static方法返回对象 调用时创建对象
     */
    private Girl2(String name) {
        this.name = name;
    }

    public static Girl2 getInstance() {
        if (g == null){
            g = new Girl2("lh");
        }
        return g;
    }

    @Override
    public String toString() {
        return "Girl{" +
                "name='" + name + '\'' +
                '}';
    }
}

饿汉式懒汉式对比

  1. 创建对象时机不同,饿汉式是在类加载的时候就创建了对象,而懒汉式是在使用时才创建对象
  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
  3. 饿汉式存在浪费资源的可能,类加载了,但没有使用实例。饿汉式使用才会创建,不会浪费

27.final

final可以修饰类、属性、方法、局部变零。

  1. 不希望类被继承
  2. 不希望父类方法子类重写
  3. 不希望类的属性被修改
  4. 不希望局部变量被修改

注意:

  1. final修饰的属性又叫常量,通常使用大写字母下划线来命名
  2. final修饰属性必须赋值,之后不能修改
    • 可以在定义时
    • 构造器
    • 代码块
  3. 如果是final修饰的是静态属性,初始化只能是在定义时和静态代码块里
  4. final类不能继承,但可以实例化
  5. 类不是final类,方法被final修饰,只是不能重写,可以继承给子类
  6. final不能修饰构造器
  7. final和static搭配使用,使用类的属性时不会导致类的加载!
  8. 包装类和String是final修饰的

28.抽象类(abstract)

当父类方法不确定时,用abstract关键字修饰,这个方法是抽象方法,用abstract来修饰类,类就是抽象类

  1. 抽象类不能实例化
  2. 抽象类不一定要包含抽象方法,其他都可以有,本质还是类
  3. 一旦有抽象方法,类必须为抽象类
  4. abstract只能修饰类和方法,不能修饰属性和其他
  5. 因为abstract必须重写,所以和final、private、static不能一起使用
  6. 继承抽象类必须重写,除非也是抽象类

可以抽出一个公共方法,然后定义不确定的抽象方法,让子类继承然后实现抽象方法,使用父类公共方法。(模板设计模式)

29.接口

接口给出未实现的方法,封装在一起,类要使用方法时,继承该接口。

接口中的方法,可以是抽象方法(可以省略abstract),默认实现方法,静态方法(jdk7之后)。

接口可以统一管理方法名,使代码规范化。

  1. 接口不能实例化。
  2. 接口中的方法是public方法,可以不用abstract修饰
  3. 抽象类实现接口,可以不实现方法
  4. 普通类实现接口,必须实现所有方法
  5. 一个类可以实现多个接口
  6. 接口中的属性是public static final修饰的
  7. 接口不能继承其他类,可以继承多个接口
  8. 接口修饰符只能是public和默认

接口的多态:

  1. 多态参数
  2. 多态数组
  3. 多态传递
package JieKou;

public class J01 {
    public static void main(String[] args) {
        C c = new C();
        /*
        ?
        ???
         */
    }
}

interface A{
    int a = new B().pr();

}

class B implements A{

    static {
        System.out.println("?");
    }
    {
        System.out.println("???");
    }

    public int  pr() {

        System.out.println("B");
        return 1;
    }
}

class C extends B{

}
package JieKou;

public class J01 {
    public static void main(String[] args) {
        new C().pr();
        // 1
        // 2
    }
}

interface A{
    int a = 1;

}

class B implements A{

    int a = 2;
}

class C extends B{
    public void pr(){
        // System.out.println(x);//Cannot resolve symbol 'x'
        System.out.println(A.a);
        System.out.println(super.a);
    }
}

接口继承比较

  1. 接口是设计方法,设计规范,让其他类实现,更加灵活,满足like-a关系,在一定程度上实现了代码的解耦
  2. 继承是解决代码的复用性和可维护性,满足is-a关系

30.内部类

30.1.局部内部类

  1. 定义在外部类的局部位置,方法或者代码块
  2. 可以直接访问外部类的所有成员
  3. 不能添加访问修饰符,但是可以使用final修饰
  4. 作用域在定义它的方法和代码块中
  5. 外部其他类不能访问
  6. 外部类和局部内部类重名,就近原则,如果需要访问外部类,使用外部类名.this.成员访问

30.2.匿名内部类

  1. 本质是类,是内部类,类系统定义名字,是一个对象,本质是继承
  2. 可以直接访问外部类的所有成员
  3. 不能添加访问修饰符
  4. 外部类和局部内部类重名,就近原则,如果需要访问外部类,使用外部类名.this.成员访问
  5. 当做实参直接传参
package NBL;

public class Niming {
    public static void main(String[] args) {
        As as = new As();
        as.a();//hhh
        as.b();//匿名内部类
    }
}

class As{

    public void a(){
        //局部内部类
        class P{
            public void p(){
                System.out.println("hhh");
            }
        }
        new P().p();
    }


    //匿名内部类
    public void b(){
        As a = new As() {
            @Override
            public void c() {
                System.out.println("匿名内部类");
            }
        };
        a.c();
    }
    public void c(){
        System.out.println("c");
    }
}

30.3.成员内部类

  1. 定义在外部类的成员位置,没有static的修饰
  2. 访问外部类所有成员
  3. 添加任意访问修饰符
  4. 外部其他类调用new外部类.new成员内部类
  5. 外部类和局部内部类重名,就近原则,如果需要访问外部类,使用外部类名.this.成员访问

30.4.静态内部类

  1. 定义在外部类的成员位置,有static的修饰
  2. 访问外部类所有静态成员
  3. 添加任意访问修饰符
  4. 外部其他类调用new外部类.成员内部类
  5. 外部类和局部内部类重名,就近原则,如果需要访问外部类,使用外部类名.成员访问
package NBL;
//成员内部类 静态内部类
public class Niming {
    public static void main(String[] args) {
        //静态内部类
        As.J j = new As().getJ();
        As.J j1 = new As.J();
        //成员内部类
        As.JD jd = new As().new JD();
    }
}

class As{
    static class J{
  
    }
    class JD{
       
    }
    public J getJ(){
        return new J();
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

就爱喝菠萝啤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值