黑马程序员Java个人笔记
BV17F411T7Ao
p121~128
目录
添加了 this() 的构造方法,虚拟机就不会再添加 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
- 代表父类存储空间