面向对象进阶:static,继承

发布于:2024-12-18 ⋅ 阅读:(48) ⋅ 点赞:(0)

黑马程序员Java个人笔记

BV17F411T7Ao

p121~128

目录

static

例:没有 static

静态变量(需要共享时使用) 

被该类所有对象共享 

static 静态变量内存图

总结

静态方法

 工具类

书写规则:类名见名知意

书写规则:私有化构造方法 

书写规则:方法定义为静态 

例:定义数组工具类

 static 的注意事项 

 静态方法中没有 this 关键字

静态方法中,只能访问静态

非静态的方法可以访问所有

静态和非静态的区别

静态

非静态

重新认识 main 方法 

继承

继承的由来

封装

出现了问题:重复

继承  

​编辑

继承 extends

什么时候用继承

继承的特点

Java 只支持单继承,不支持多继承,但支持多层继承

每一个类都直接或间接地继承于 Object

子类对父类的继承 

构造方法私有非私有都不能被子类继承

成员变量都能被继承,私有的不能直接使用

非私有成员变量继承的内存图

私有成员变量继承的内存图

成员方法中的虚方法可以被继承,私有不能

继承中子类对父类的访问特点

成员变量的访问特点

​编辑

成员方法的访问特点

方法的重写

 方法重写的本质

方法重写的注意事项

构造方法的访问特点 

子类怎么调用父类构造方法

子类构造方法的第一行是 super()

想要创建对象的时候,就把属性值赋值过去

总结 

this,super 使用总结 

this

this 理解为一个变量 

this 访问本类其他构造方法(做默认值时使用)

添加了 this() 的构造方法,虚拟机就不会再添加 super() 了

super


 

static

例:没有 static

// JavaBean类
public class Student {
    private String name;
    private int age;
    private String gender;
    public String teacherName;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getTeacherName() {
        return teacherName;
    }

    public void setTeacherName(String teacherName) {
        this.teacherName = teacherName;
    }

    public void study(){
        System.out.println(name+"正在学习");
    }

    public void show(){
        System.out.println(name+", "+age+", "+gender+", "+teacherName);
    }
}
// 测试类
public class StudentTset {
    public static void main(String[] args) {
        // 创建第一个学生对象
        Student s1 = new Student();
        s1.setName("zhangsan");
        s1.setAge(20);
        s1.setGender("male");
        s1.teacherName = "Mr Li";

        s1.study();
        s1.show();

        // 创建第二个学生对象
        Student s2 = new Student();
        s2.setName("lisi");
        s2.setAge(20);
        s2.setGender("female");

        s2.study();
        s2.show();
    }
}
  • teacherName 是每一个对象的属性 
  • 现在只给第一个 s1 对象赋值了,所以有值
  • 第二个 s2 对象没赋值,所以是 null

  • 这不合理,因为一个班所有的学生应是共享同一个老师的 

静态变量(需要共享时使用) 

  • 静态变量推荐使用类名调用
  • 因为静态不属于对象,还用对象调用,不合理

被该类所有对象共享 

// javabean类
public class Student {
    private String name;
    private int age;
    private String gender;
    private static String teacherName; // 加上static
}

static 静态变量内存图

  • 程序刚开始启动,main 方法先进栈


  • 然后是 main 方法当中的第一行代码
    • 在这行代码当中,用类名(Student)调用了静态变量 teacherName,并赋值“阿玮老师”
  • 用到了 Student 这个类,在内存中,就会把 Student 这个类的字节码文件加载到堆内存中(jdk8以后),并在内存中,创建了一个单独存放静态变量的空间(静态区)
    • teacherName 引用数据类型,默认初始化值 null
  • 内存中没有对象,静态变量随着类的加载而加载,优先于对象出现


  • 第一行赋值完成,静态区中的 null 被覆盖成“阿玮老师”


  • 第二行,有 new 关键字了,在堆中开辟了一个空间(即对象) ,在这空间里存储所有的非静态的成员变量,并进行默认初始化
  • 再把小空间的地址赋值给 s1


  • 赋值


  • 用 s1 调用 show() 方法,show() 方法调用进栈 
  • 因为 show 方法的调用者是 s1, 就会通过 s1 去找 name,age
  • 然后再去找到静态区中的 teacherName


  • 方法执行完毕,出栈


  • 创建 s2,new,在堆中开辟空间
  • 通过 s2 调用 show 方法,show 方法加载进栈。通过 s2 找属性,然后再到静态区中找 teacherName 


  • show 方法执行完成,出栈

总结
  • 静态区中的变量,是对象共享的,在内存中只有一份。而非静态的变量,每一个对象(每一个小空间)里单独存放的 

静态方法

 工具类

书写规则:类名见名知意
  • util,即 utility,工具
public class ArrUtil {

}
书写规则:私有化构造方法 
  • 构造方法私有后,外界不能创建这个类的对象
  • 因为工具类不是描述一类事物的,创建它的对象没有意义
public class ArrUtil {

    private ArrUtil() {

    }
}
书写规则:方法定义为静态 
  • 方法定义为静态,方便调用
例:定义数组工具类

// 工具类
public class ArrayUtil {

    // 私有化构造方法
    // 目的:为了不让外界创建它的对象
    private ArrayUtil() {}

    // 工具方法定义为静态的,方便调用
    // 拼接数组方法
    public static String printArr(int[] arr){
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < arr.length; i++) {
            if(i == arr.length-1){
                sb.append(arr[i]);
            }else{
                sb.append(arr[i]+",");
            }
        }
        sb.append("]");
        return sb.toString(); // 把StringBuilder变回字符串
    }

    // 返回平均值方法
    public static double getAverage(double[] arr){
        double sum = 0;
        for(int i = 0; i < arr.length; i++){
            sum += arr[i];
        }
        return sum/arr.length;
    }
}
// 测试类
public class TestDemo {
    public static void main(String[] args) {
        // 测试工具类中的两个方法是否正确

        int[] arr1 = {1, 2, 3, 4, 5};
        String str = ArrayUtil.printArr(arr1);
        System.out.println(str);

        double[] arr2 = {11.4, 5.14, 1.919, 8.10};
        double avg = ArrayUtil.getAverage(arr2);
        System.out.println(avg);
    }
}

 static 的注意事项 

 静态方法中没有 this 关键字

  • 即,非静态方法中,有 this
    • this 表示当前方法调用者的地址值
    • 这个 this 是隐藏的,由虚拟机赋值

  • s1 调用的 show1 方法时,虚拟机就会把 s1 赋值给 this
  • s2 调用时,把s2 赋值给 this 


  • 在调用成员变量时,前面有隐含的 this。在方法中(即局部位置),没有和成员位置重名,所以 this 可以省略不写


  • 静态方法中没有 this 关键字
  • 因为非静态的东西,一般和对象相关,比如 this.name,表示对象的 name
  • 静态是共享的,和某一个对象没有关系。所以静态方法中,没有 this 关键字

静态方法中,只能访问静态

  • 即,静态方法中,不能访问非静态的东西(成员变量,成员方法)

  •  在静态方法中,强行调用 name,会报错
  • 因为不知道是哪个对象的 name,要有 this. 才能表示是哪个对象的,但静态方法中没有 this 

非静态的方法可以访问所有

  • 在调用 teacherName (静态变量)时,前面也有隐含的 this
  • 静态有类名和对象两种调用方式,所以用对象也能访问到共享的数据

静态和非静态的区别

静态

  • 静态随着类的加载而加载,类加载到内存当中后,静态就有了

  • 静态加载到内存中后,没有创建对象(即内存中没有非静态对象)

  • 静态的东西可以相互调用

非静态

  • 非静态和对象有关,只要没有创建对象,非静态的数据就不会出现在内存当中
  • 所以静态无法调用非静态

重新认识 main 方法 

  • public,权限修饰符,main 方法是被虚拟机调用的,访问权限要足够大
  • static,表示 main 方法是静态的,被虚拟机调用的时候,不需要创建对象,直接类名就可以调用
    • 因为 main 方法是静态的,所以测试类中其他方法也需要是静态的 

继承

  • 面向对象的三大特征:封装、继承、多态

继承的由来

封装

  • 有了封装之后,就可以把一些零散的数据和一些行为封装成一个整体,这个整体就是「对象」
    • 封装的好处:如果想要打印学生的信息,没有封装,就需要把零散的数据都要单独的传递给方法,方法就要写很多参数;有了封装,可以把整体传递给方法,方法需要什么参数,就可以用过 get 方法获取出来

出现了问题:重复

  • 当有第二个 Javabean 类描述老师,重复的代码太多了

继承  

  • 类和类间的父子关系

继承 extends

什么时候用继承

  • 错误案例:子类不是父类的中的一种

继承的特点

Java 只支持单继承,不支持多继承,但支持多层继承

  • 支持单继承:一个子类只能继承一个父类


  • 不支持多继承:子类不能同时继承多个父类


  • 多层继承:子类 A 继承父类 B,父类 B 可以继承父类 C 

每一个类都直接或间接地继承于 Object

  • 正常


  • 如果没写父类,虚拟机会加上默认的继承关系,继承于 Object 类

子类对父类的继承 

构造方法私有非私有都不能被子类继承

  • 如果能被继承,就违背了构造方法定义,即构造方法名要和类名一致

成员变量都能被继承,私有的不能直接使用

  • 因为被 private 修饰,所以无法赋值
  • 只要是成员变量,不管是私有的,还是非私有的,在子类里都能继承下了,只不过私有的虽然继承下来,但不能直接使用
非私有成员变量继承的内存图
  • 测试类 TestStudent 这个类的字节码文件加载到堆内存中(jdk8以前是方法区,以后的加载到内存中),这里面存储的是 main 方法 


  •  main 方法被虚拟机自动调用,进栈


  • main 中的第一行代码使用 Zi 这个类,把 Zi 这个类的字节码文件加载到内存当中
  • 因为 Zi 这个类继承于 Fu,把 Fu 这个类的字节码加载的内存中
  • 如果一个类没有写继承关系,虚拟机添加一个默认的父类 Object。所以在内存中还会加载 Object 的字节码文件,但这里没写


  • = 的左边相当于在栈里面声明了一个变量
  • new 在堆里面开辟了一个空间(即对象)
  • 对象(开辟的空间)里面一分为二,分别记录父和子中的成员变量
  • 默认初始化


  • 打印,赋值  

私有成员变量继承的内存图
  • name 和 age 被 private 修饰,直接调用,找不到

成员方法中的虚方法可以被继承,私有不能

  • 成员方法通过虚方法表进行继承
  • 虚方法
    • 非 private、非 static、非 final

继承中子类对父类的访问特点

成员变量的访问特点

  • this 在本类成员位置找
  • super 在父类成员位置找

成员方法的访问特点

  • 直接调用满足就近原则
  • super 调用,直接访问父类
方法的重写
  • 只有虚方法表中的方法可以被重写

 方法重写的本质
  • 方法重写的本质就是覆盖虚方法表中的方法

方法重写的注意事项

构造方法的访问特点 

  • 父类中的构造方法不会被子类继承
    • 如果继承了,就不满足构造方法和类名一致的原则,所以不能不能继承
  • 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己

  •  父类中的属性都是共性的内容,都可以被子类继承的,而父类中的空参构造相当于给父类中的属性进行默认初始化
  • 如果没有这一步,那么子类在使用父类中的属性时,就没有值了

子类怎么调用父类构造方法

子类构造方法的第一行是 super()
  • 子类构造方法的第一行是super(),不写也存在,且必须在第一行


  • 创建对象时,括号里什么都没写。即现在通过无参构造创建对象


  • 在空参构造的第一行,又有 super(),所以要去调用父类的无参构造 

想要创建对象的时候,就把属性值赋值过去
  • 在子类中,就要写上对应的带参构造 


  • 里面的内容如果不写,子类构造方法的第一句默认是 super(),会自动调用父类的空参构造,没有用

  •  要手动调用父类的带参构造

总结 

this,super 使用总结 

this

  • 理解为一个变量,表示当前方法调用者的地址值

this 理解为一个变量 

  • 当 show 被调用的时候,虚拟机就会把调用者的地址值赋值给 this,就表示右边这个 s 的对象 

  • 而下面在调用成员变量的时候,前面也有隐含的 this 

this 访问本类其他构造方法(做默认值时使用)

  • 如果用空参的方式来创建对象,这个时候,就会继续调用本类的其他构造。相当于给了默认值 

添加了 this() 的构造方法,虚拟机就不会再添加 super() 了
  • 因为 this() 表示调用本类的其他构造
  • 在其他构造的默认的第一行,有一个隐藏的 super()
  • 在子类的构造方法中,其实只要有一个构造去访问父类就可以了
    • this() 调用本类其他构造,其他构造里有 super(),写 this() 的这个类就不用了

  • 这里的 this() 和 super() 一样,必须写在第一行

super

  • 代表父类存储空间


网站公告

今日签到

点亮在社区的每一天
去签到