Skip to content

一、复习

1.1 概念名称(以面向对象之后为主)

1、问题 1:成员变量与局部变量

成员变量成员变量局部变量
看有没有 static 修饰静态变量(有)实例变量(没有)局部变量(没有)
如何区分?在方法外在方法外在方法里面
跨类如何访问类名.静态变量先创建对象,然后通过 对象名.实例变量无法跨类使用
是否有默认值
作用域本类随便用本类静态方法不能直接使用本类的实例变量
值是否共享被本类所有对象共享每一个对象都是独立的仅限于当前方法
在内存中存在什么位置JDK7 及其之前:方法区永久代
JDK8 及其之后:堆
生命周期从这个类被加载到内存就开始了从 new 对象开始,到 GC(垃圾回收)结束随方法入栈到方法出栈

image-20250707085435846

2、构造器

构造器的特点:

  • 构造器的名字必须与类名完全一致
  • 构造器没有返回值类型,一定不能写返回值类型
  • 构造器前面不能加 static 等其他修饰符,只能加 public、protected、缺省、private
  • 如果程序员没有手动编写任何构造器,那么它会有一个默认的无参构造
  • 如果程序员手动编写了构造器,那么你写几个构造器,它就有几个构造器
  • 一个类如果有多个构造器,那么多个构造器之间是重载关系
  • 构造器是用来在 new 对象的时候,为实例变量进行初始化

生成构造器的默认快捷键:Alt + Insert,选 Constructor

3、成员方法

静态方法实例方法
有没有 static没有
本类中只能使用本类的静态成员本类的所有成员都可以
跨类类名.静态方法对象名.实例方法

本类的成员之间互相使用就遵循一个原则:静态不能使用非静态,其他随便用。

跨类使用成员(关于静态非静态)也只有一个原则:静态的用 “类名.”,非静态的先 new 对象,再用“对象名.”

4、面向对象的基本特征之一:封装

属性私有化是什么意思?

  • 第一步:在成员变量之前加 private
  • 第二步:提供 get/set 方法
    • get:供类的外面来获取一个属性值
    • set:供类的外面来修改一个属性值

5、权限修饰符有 4 种情况,3 个单词

权限修饰符有的人翻译为访问控制修饰符。

这些权限修饰符的可见性范围如下:

本类本包跨包的子类跨包的非子类
private×××
缺省××
protected×
public

二、对象数组

对象数组是用来存储一组对象的。

2.1 一维对象数组的声明和初始化

静态初始化语法格式:

java
数据类型[]  数组名 = {对象1,对象2,对象3};
//这里的数据类型是除了8种基本数据类型以外,通常是类

动态初始化语法格式:

java
数据类型[] 数组名 = new 数据类型[长度];
//这里的数据类型是除了8种基本数据类型以外,通常是类

动态初始化,数组元素是默认值。

元素的类型默认值
byte0
short0
int0
long0L
float0.0F
double0.0
char\u0000
booleanfalse
引用数据类型null
java
给对象数组的元素赋值:
    数组名[下标] = 对象;

2.2 一维对象数组的遍历

1、普通 for 循环

快捷键:数组名.fori

java
for(int i=0; i<数组名.length; i++){
    数组名[i]就是元素,此时元素是对象
}

2、增强 for 循环(请看 2.7 小节)

2.3 示例代码

java
package com.atguigu.array;

public class Student {
    //实例变量
    private String name;//姓名
    private int score;//成绩

    //无参构造
    public Student() {
    }
    //有参构造
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    //get/set方法
    public String getName() {
        return name;
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    //其他方法
    public String display(){
        return "姓名:" + name +",成绩:" + score;
    }
}

静态初始化:

java
package com.atguigu.array;

public class TestStudent {
    public static void main(String[] args) {
        //Student[] students = {new Student("张三",89), new Student("李四",84), new Student("王五",90)};

        Student s1 = new Student("张三", 89);
        Student s2 = new Student("李四",84);
        Student s3 = new Student("王五",90);
        Student[] students = {s1, s2, s3};

        for (int i = 0; i < students.length; i++) {
           // System.out.println(students[i]);//这么写看到的是3个对象地址值
          //  System.out.println("姓名:" + students[i].getName() +",成绩:" + students[i].getScore());
            System.out.println(students[i].display());
        }
    }
}

动态初始化:

java
package com.atguigu.array;

public class TestStudent2 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        //上面这句代码创建的不是学生对象,而是数组对象
        //比喻:上面这句代码准备了3个停车位,此时停车位上没有车
        //     或者准备了3个新座位给同学,但是同学来没来

        //此时数组的元素都是默认值null
        for (int i = 0; i < students.length; i++) {
            System.out.println(students[i]);//null
//            System.out.println(students[i].display());
// 错误,因为students[i]是null,null表示没有指向任何学生对象,就无法获取到学生对象的姓名和成绩信息
            //会发生空指针异常NullPointerException
        }

        //学生报到
        students[0] = new Student("张三", 89);
        students[1] = new Student("李四",84);
        students[2] = new Student("王五",90);

        //再次遍历
        for (int i = 0; i < students.length; i++) {
//            System.out.println(students[i]);//地址值
            System.out.println(students[i].display());
        }
    }
}

2.4 一维数组的算法

1、找最大值

java
package com.atguigu.array;

public class TestStudent3 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        //学生报到
        students[0] = new Student("张三", 89);
        students[1] = new Student("李四",84);
        students[2] = new Student("王五",90);

        //找出成绩最高的学生
        //(1)第一步,先假设第1个元素最大
        Student max = students[0]; //max现在代表一个学生对象
        //(2)第二步:用max与剩下的元素挨个比较
        for (int i = 1; i < students.length; i++) {
            //比较:两个学生的成绩
//            if(max < students[i])//错误, max和students[i]都是学生对象的首地址,地址值是无法比较大小的
            if(max.getScore() < students[i].getScore()){
                max = students[i];
            }
        }

        //成绩最高的学生
//        System.out.println("成绩最高的学生:" + max);//地址值
        System.out.println("成绩最高的学生:" + max.display());//地址值

    }
}

2、冒泡排序

java
package com.atguigu.array;

public class TestStudent4 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        //学生报到
        students[0] = new Student("张三", 89);
        students[1] = new Student("李四",84);
        students[2] = new Student("王五",90);

        //按照成绩从低到高
        //冒泡排序:(1)相邻元素比较(2)n个元素需要比较n-1轮
        for(int i=1; i<students.length; i++){
            /*
            当i=1,
                students[0] ~ students[1]
                students[1] ~ students[2]
                j = 0,1  j<3-1
            当i=2
                students[0] ~ students[1]
                j=0     j<3-2

                students[j] ~ students[j+1]
             */
            for(int j=0; j<students.length - i; j++){
//                if(students[j] > students[j+1]){//错误, students[j] 和 students[j+1]里面是地址值
                if(students[j].getScore() > students[j+1].getScore()){
                    //交换  students[j] 和 students[j+1]
                    Student temp = students[j];
                    students[j] = students[j+1];
                    students[j+1] = temp;
                }
            }
        }

        //显示排序后的结果
        for (int i = 0; i < students.length; i++) {
            System.out.println(students[i].display());
        }

    }
}

2.5 对象数组的内存分析

image-20250707105252587

2.6 答疑:关于冒泡排序的 i 从 0 还是 1 开始

image-20250707111615297

image-20250707111631300

2.7 增强 for 循环

2.7.1 增强 for 循环的语法格式

增强 for 循环,又称为 foreach 循环。

增强 for 循环是一种语法糖,用于简化开发人员遍历数组或集合(集合是后面章节才会学习)的代码。

增强 for 循环的,快捷键:iter

java
for(元素的类型 元素名 : 数组名){

}

对比:

普通 for 循环,快捷键:数组名.fori

java
for(int i=0; i<数组名.length; i++){
    数组名[i]就是元素,此时元素是对象
}

语法糖(Syntactic Sugar)是指编程语言中为方便开发者书写代码而提供的一种简化写法,它不会改变程序的功能,但在形式上更加简洁、易读。编译器或解释器在处理这些语法糖时,会将其转换为更基础的语法结构。

for-each 循环:

  • for(String s : 数组名),底层实际上是通过普通 for 循环实现的。

  • for (String s : 集合名),底层实际上是通过迭代器实现的。(后面集合章节再讲)

image-20250707113117102

java
package com.atguigu.foreach;

public class TestArray {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30, 40, 50};
        //普通for循环,快捷键  数组名.fori 或 itar
        //遍历:挨个看看
        System.out.println("普通for循环遍历:");
        for (int i = 0; i < arr.length; i++) {//i可以当做数组下标使用
            System.out.println(arr[i]);
        }

        //增强for循环
        System.out.println("增强for循环遍历:");
        //快捷键 iter
        for (int i : arr) {//i是元素
            System.out.println(i);
        }
    }
}
java
package com.atguigu.foreach;

import com.atguigu.array.Student;

public class TestObjectArrays {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("张三", 89);
        students[1] = new Student("李四",84);
        students[2] = new Student("王五",90);

        //普通for循环
        for (int i = 0; i < students.length; i++) {
            System.out.println(students[i].display());
        }
        System.out.println("=========================");
        //增强for循环
        for (Student student : students) {
            System.out.println(student.display());
        }

    }
}

2.7.2 普通 for 循环与增强 for 循环的区别

普通 for 循环与增强 for 循环的区别:

普通 for 循环增强 for 循环
语法格式for(int i=0; i<数组名.length; i++){
数组名[i]就是元素,此时元素是对象
}
for(元素的类型 元素名 : 数组名){

}
是否有下标信息
是否可以替换元素可以不可以
是否可以修改元素的属性值可以可以(只要不换对象)
如何选择既要看元素, 又可能要修改元素,就用普通 for 循环如果只是看元素,建议用增强 for 更简洁
适用场景除了遍历数组,在其他需要重复执行代码的地方也可以用普通 for仅仅只能用于遍历数组或集合

image-20250707113726832

java
package com.atguigu.foreach;

public class TestDifferent {
    public static void main(String[] args) {
        //区别
//        (1)普通for循环有下标,增强for循环没有下标
//        (2)普通for循环可以替换修改元素,增强for循环不可以替换修改元素

        int[] arr = {10, 20, 30, 40, 50};
        //普通for循环
        for (int i = 0; i < arr.length; i++) {
            arr[i] *= 2;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
        System.out.println();

        int[] nums = {10, 20, 30, 40, 50};
        //增强for循环
        for (int element : nums) {
            element *= 2;
        }
        for (int element : nums) {
            System.out.print(element+" ");
        }
        System.out.println();
    }
}
java
package com.atguigu.foreach;

import com.atguigu.array.Student;

public class TestObjectArrays2 {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("张三", 89);
        students[1] = new Student("李四",84);
        students[2] = new Student("王五",90);

        //普通for循环
        for (int i = 0; i < students.length; i++) {
//            students[i].setScore(100);//修改原对象的属性
            students[i] = new Student(students[i].getName(), 100);//替换对象
        }
        for (int i = 0; i < students.length; i++) {
            System.out.println(students[i].display());
        }
        System.out.println("=========================");
        Student[] all = new Student[3];
        all[0] = new Student("张三", 89);
        all[1] = new Student("李四",84);
        all[2] = new Student("王五",90);
        //增强for循环
        for (Student student : all) {
//            student.setScore(100);//修改原对象的属性
            student = new Student(student.getName(),100);//替换对象
        }
        for (Student student : all) {
            System.out.println(student.display());
        }
    }
}

三、面向对象的基本特征之二:继承

3.1 什么是继承?

生活中:财产的继承、文化的传承、基因的延续

继承的含义:

  • 延续:代码的复用
  • 扩展:增加新的成员、功能的改写

3.2 为什么要继承?

  • 代码的复用

  • 类与类之间有 is-a 的关系

    • Student is a Person. 学生是人的一个子类别。
    • Teacher is a Person. 老师也是人的一种。
  • 对父类进行扩展

没有继承,重复代码很多

java
package com.atguigu.inherited;

public class Person {
    //这里故意先不私有化
    public String name;
    public int age;

    public void eat(){
        System.out.println("吃东西");
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age;
    }
}
java
package com.atguigu.inherited;

public class Student {
    //这里故意先不私有化
    public String name;
    public int age;
    public int score;

    public void eat(){
        System.out.println("吃东西");
    }

    public void study(){
        System.out.println("学习");
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age +",成绩:" + score;
    }
}
java
package com.atguigu.inherited;

public class Teacher {
    //这里故意先不私有化
    public String name;
    public int age;
    public double salary;

    public void eat(){
        System.out.println("吃东西");
    }

    public void teach(){
        System.out.println("讲课");
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age +",薪资:" + salary;
    }
}

改为代码继承的方式:

3.3 如何继承?

语法格式:

java
【修饰符】 class 子类名 extends 父类名{

}
java
package com.atguigu.inherited;

public class Person {//父类
    //这里故意先不私有化
    public String name;
    public int age;

    public void eat(){
        System.out.println("吃东西");
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age;
    }
}
java
package com.atguigu.inherited;

//extends:扩展,继承
public class Student extends Person{//Student是Person的子类
    //这里故意先不私有化
    public int score;

    public void study(){
        System.out.println("学习");
    }

    public String getInfo(){
        return super.getInfo() + ",成绩:" + score;
    }
}
java
package com.atguigu.inherited;

public class Teacher extends Person{//Teacher是Person的子类
    //这里故意先不私有化
    public double salary;

    public void teach(){
        System.out.println("讲课");
    }

    public String getInfo(){
        return super.getInfo() +",薪资:" + salary;
    }
}
java
package com.atguigu.inherited;

public class TestStudentTeacher {
    public static void main(String[] args) {
        //创建Student类对象
        Student s = new Student();
        s.name = "张三";//从父类继承的name属性的声明
        s.age = 23;//从父类继承的age属性的声明
        s.score = 89;//子类自己声明
        System.out.println(s.getInfo());
        s.eat();//从父类继承的eat方法声明
        s.study();//子类自己声明

        //创建Teacher类的对象
        Teacher t = new Teacher();
        t.name = "柴老师";
        t.age = 18;
        t.salary = 15000;
        t.eat();
        t.teach();
        System.out.println(t.getInfo());
    }
}

3.4 继承的特点和要求

1、哪些成员会继承到子类中?

  • 从可操作性的角度来说:只有非私有的属性、方法才会继承到子类中,即子类对象或子类中才能直接操作。
  • 但是从事物特征的角度来说:所有属性、方法都会继承到子类中。

2、单独说构造器,构造器会继承吗?

  • 构造器不能直接被继承。父类定义了 n 个构造器,不代表子类也有 n 个构造器。
  • 但是,子类构造器一定一定调用父类的构造器
    • 子类构造器默认找的是父类的无参构造。如果父类没有无参构造,怎么解决?(1)给父类加无参构造(2)手动调用父类的有参构造
    • super(); 这句代码通常会被省略,调用父类的无参构造
    • super(参数); 这句代码用于调用父类的有参构造,这个不能省略。
    • super()super(参数) 代码必须在子类构造器的首行。

为什么子类构造器中要调用父类的构造器?

构造器的作用是在 new 对象时,为对象的属性初始化。

从事物特征的角度来说,子类会从父类继承所有的属性,那么这些属性都要初始化。可以通过父类已经定义好的构造器,直接为他们初始化,不需要重复写一遍了。

image-20250707152206372

3、Java 中的继承有单继承的限制。

比喻:只有 1 个亲生父亲

C++语言中有多重继承。Java 不能多重继承。

4、Java 的类允许多层继承

比喻:遵循代代相传

如何查看类的继承关系树?选择一个类名,按快捷键 Ctrl + H

5、Java 类的根父类是 Object

如果一个类没有写它的父类是谁,默认它的父类就是 Object

6、一个父类可以同时有多个子类

示例代码 1:

java
package com.atguigu.inherited;

public class Graphic {//图形
    //定义一个私有的属性:name
    private String name;

    public Graphic() {
        System.out.println("Graphic的无参构造");
    }

    public Graphic(String name) {
        this.name = name;
        System.out.println("Graphic的有参构造");
    }

    public String getName() {
        return name;
    }

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

    public String getInfo(){
        return "图形名称:" + name;
    }
}
java
package com.atguigu.inherited;

public class Rectangle extends Graphic{//矩形
    private double length;//长
    private double width;//宽

    //构造器
    public Rectangle() {
       // 这里默认调用父类Graphic的无参构造。省略了super();
    }

    public Rectangle(String name, double length, double width) {
        //如果没写 super(name); 默认也找父类的无参构造
        super(name);
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public String getInfo(){
//        return "矩形名称:" + name ;//父类的name是private修饰,子类不能直接使用
        return "矩形名称:" + getName() +",长:" + length +",宽:" + width;
        //间接使用父类声明的name属性
    }
}

示例代码 2:

java
package com.atguigu.inherited;

public class Father {
    public int money;
}
java
package com.atguigu.inherited;

public class Person extends Father{//父类
    //这里故意先不私有化
    public String name;
    public int age;
    //私有化
    private char gender;//性别

    //构造器
    //无参构造
    public Person() {
    }

    //有参构造
    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public char getGender() {
        return gender;
    }

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

    public void eat(){
        System.out.println("吃东西");
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age +",性别:" + gender;
    }
}
java
package com.atguigu.inherited;

//extends:扩展,继承
public class Student extends Person{//Student是Person的子类
    //这里故意先不私有化
    public int score;

    //子类的无参构造
    public Student(){
        super();//调用父类的无参构造。这句代码可以省略。
    }

    //子类的有参构造
    public Student(String name, int age, char gender, int score) {
        super(name, age, gender);//调用父类的有参构造
        this.score = score;
    }

    public void study(){
        System.out.println("学习");
    }

    public String getInfo(){
        return super.getInfo() + ",成绩:" + score;
       // return "姓名:" + name +",年龄:" + age + ",性别 :" + gender +",成绩:" + score;//错误,因为gender在父类是私有的
       // return "姓名:" + name +",年龄:" + age + ",性别 :" + getGender() +",成绩:" + score;//通过getGender()间接访问
    }
}
java
package com.atguigu.inherited;

public class Teacher extends Person{//Teacher是Person的子类
    //这里故意先不私有化
    public double salary;

    //子类的无参构造
    public Teacher() {
        super();//调用父类的无参构造
    }

    //子类的有参构造
    public Teacher(String name, int age, char gender, double salary) {
        super(name, age, gender);//调用父类的有参构造
        this.salary = salary;
    }

    public void teach(){
        System.out.println("讲课");
    }

    public String getInfo(){
        return super.getInfo() +",薪资:" + salary;
    }
}
java
package com.atguigu.inherited;

public class TestStudentTeacher {
    public static void main(String[] args) {
        //创建Student类对象
        Student s = new Student();
        s.name = "张三";//从父类继承的name属性的声明
        s.age = 23;//从父类继承的age属性的声明
        s.score = 89;//子类自己声明

        //        s.gender = '男';//不能直接操作,因为private仅限于本类内部操作
        s.setGender('男');//可以间接操作gender

        s.eat();//从父类继承的eat方法声明
        s.study();//子类自己声明
        System.out.println(s.getInfo());//子类改写了getInfo()方法

        System.out.println("=============================");

        //创建Teacher类的对象
        Teacher t = new Teacher();
        t.name = "柴老师";//从父类继承的name属性的声明
        t.age = 18;//从父类继承的age属性的声明
        t.salary = 15000;//子类自己声明

        t.eat();//从父类继承的eat方法声明
        t.teach();//子类自己声明
        System.out.println(t.getInfo());//子类改写了getInfo()方法

        System.out.println("=============================");
        s.money = 2000000;//从爷爷类Father继承的money属性的声明
        t.money = 2000000;//从爷爷类Father继承的money属性的声明
    }
}

3.5 方法的重写

当子类继承了父类的某个方法后,觉得父类方法的方法体实现不适用于子类,子类对其进行重新实现,称为重写,即在子类中覆盖父类的方法体。在重写过程中,想要复用父类被重写方法的方法体,可以通过 super.父类方法()的方式。

方法的重载(Overload)方法的重写(Override)
位置同一个类中 或 父子类中父子类
方法名相同相同
形参列表不同 (形参的个数、类型、顺序等不同)相同
返回值类型无关(1)如果是基本数据类型或 void:相同
(2)如果是引用数据类型,需要满足 <=的关系
权限修饰符(public 等)无关必须满足 >= 的关系,子类方法权限修饰符的可见性范围要比父类大

image-20250707151915920

image-20250707152411399

问题:父类大还是子类大?

这里的大小是从这个类代表的事物的范围角度来说的。

父类是 Person,代表是人类

子类是 Student,代表的是学生类

人的范围 > 学生的范围

示例代码 1

java
package com.atguigu.override;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    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 getInfo(){
        return "姓名:" + name +",年龄:" + age;
    }
}
java
package com.atguigu.override;

public class Student extends Person{
    private int score;

    public Student() {
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    //重新父类方法的快捷键
    //(1)Ctrl + O(推荐)
    //(2)或Alt + Insert 再选Override Methods
    /*
    重写方法的上面标记 @Override的作用?
    (1)可读性更好:人家一看就知道这是一个重写的方法
    (2)可以让编译器对它进行更加严格的格式校验,如果不符合重写的要求,直接报错。

    但是,如果一个方法并未违反重写的要求,那么加@Override 或不加 不影响它是重写的本质。
     */
    @Override
    public String getInfo() {
//        return "姓名:" + name +",年龄:" + age +",成绩:" + score;
        //私有的子类不能直接用
        //return "姓名:" + getName() +",年龄:" + getAge() +",成绩:" + score;

//        return getInfo()+",成绩:" + score;//错误,这是无条件递归调用
        return super.getInfo() +",成绩:" + score;
                //super.getInfo()等价于 父类getInfo()方法的方法体
        //即等价于 return "姓名:" + name +",年龄:" + age;
    }
}
java
package com.atguigu.override;

public class TestStudent {
    public static void main(String[] args) {
        Student s = new Student("张三",23,100);
        System.out.println(s.getInfo());
    }
}

示例代码 2

java
package com.atguigu.override;

public class Base {//父类,又称为基类
    public void m1(){
        //....不关心方法体
    }
    public double m2(){
        return 0.0;
    }

    public Object m3(){
        return null;
    }

    protected void m4(){

    }

    public void print1n(){

    }
}
java
package com.atguigu.override;

public class Sub extends Base{
    public void m1(){ //重写时:返回值类型必须是void
        //....不关心方法体
    }
    public  double m2(){//重写时:返回值类型必须是double
        return 0.0;
    }

    //Object > String
    public String m3(){//重写时:返回值类型可以是Object或Object的子类
        return null;
    }

    public void m4(){//重写时:权限修饰符可以是protected或public

    }

/*    @Override
    public void println(){ //方法名不一样,不是重写

    }*/
}

3.6 Object 类:根父类

所有类都是 Object 的子类。Object 类的所有方法,子类都会继承。

Object 类中一共有 11 个方法,咱们今天只学习 1 个,public String toString(),重写 toString 快捷键建议用 Alt + Insert

如果我们用System.out.println()System.out.print()打印一个对象,会自动调用对象的 toString 方法。

如果我们把对象与 字符串进行拼接,也会自动调用对象的 toString 方法。

建议所有 Object 的子类都重写这个方法。

image-20250707154529263

image-20250707154704347

查看某个类的源代码:Ctrl + N,搜索这个类

查看某个类的成员列表:Ctrl + F12,部分同学需要按 Fn

image-20250707155641511

image-20250707155709492

java
package com.atguigu.object;

public class Person {
    //属性私有化
    private String name;
    private int age;

    //无参构造
    public Person() {
    }

    //有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //get/set
    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;
    }

    //重写toString,
    // 快捷键(1)Alt + Insert,选toString (2)Ctrl + O
    /*
    以下代码是Ctrl + O生成的代码模板
    @Override
    public String toString() {
        return super.toString();
    }
     */
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
java
package com.atguigu.object;

public class Student extends Person{
    //实例变量的属性私有化
    private int score;

    //无参构造
    public Student() {
    }

    //有参构造
    public Student(String name, int age, int score) {
        super(name, age);
        this.score = score;
    }

    //get/set
    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    //重写toString
    @Override
    public String toString() {
        return "Student{" +
                super.toString()+
                "score=" + score +
                "} "  ;
    }
}
java
package com.atguigu.object;

public class TestStudent {
    public static void main(String[] args) {
        Student s = new Student("张三",23,96);
        System.out.println(s);
        //当我们打印对象时,JVM会自动帮我们调用toString方法
        //如果我们的类没有重写toString,就会找Object的toString方法
        //如果没有重写toString,打印结果是:com.atguigu.object.Student@41629346
        /*
        Object的toString的源码:
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
        getClass().getName():获取类的名字
        hashCode():获取对象的哈希值,比喻,对象的哈希值就像对象的身份证号码,就是一个编号。
        Integer.toHexString(hashCode()):把哈希值用十六进制形式显示出来
         */

        System.out.println(s.hashCode());//1096979270  十进制
        //对应的十六进制:41629346
    }
}

四、关键字

4.1 final 关键字

final 代表最终的。

final 在 Java 中是一个修饰符,可以用来修饰类、成员变量、成员方法、局部变量。不能修饰构造器。

(1)修饰类:这个类就是一个太监类。不能有子类。例如:String、System、Math 类都是 final,它们非常重要,功能要明确的,稳定的,不允许别人继承,对其进行改写、扩展等。

(2)修改方法:这个方法不允许被重写。

(3)修饰局部变量:这个变量的值不能被修改,它是固定的常量。

(4)修饰成员变量:这个成员变量的值不能被修改,它必须显式初始化,它们不能使用默认值。

  • 如果是静态的成员变量加 final,一般写成大写的
  • 如果是非静态的成员变量加 final,必须在构造器中初始化,习惯上用有参构造为他们初始化,如果有无参构造,必须手动给它们指定值。
  • final 修饰的成员变量,不管静态非静态,都没有 set 方法。

image-20250707171137811

java
package com.atguigu.keyword;

public class TestVariable {
    public static void main(String[] args) {
        int a = 1;
        a = 2;

        final int b = 1;
       // b = 2;//不允许重新赋值,修改它的值
    }
}
java
package com.atguigu.keyword;

public class Triangle {//这种写法,语法没问题,但是没什么意义
    private final int a = 1;
    private final int b = 1;
    private final int c = 1;

    public Triangle() {
    }
/*    public Triangle(int a,int b,int c) {//此时不能写有参构造
        this.a = a;
        this.b = b;
        this.c = c;
    }*/


    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                '}';
    }
}
java
package com.atguigu.keyword;

public class Triangle {
    private static final String NAME = "三角形";
    private final int a ;
    private final int b;
    private final int c ;

    public Triangle(){//此时一般不写无参构造,如果要写,必须手动指定a,b,c的值
        a = 1;
        b = 1;
        c = 1;
    }
    public Triangle(int a,int b,int c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                '}';
    }
}

image-20250707171738815

java
package com.atguigu.keyword;

public class TestTriangle {
    public static void main(String[] args) {
        Triangle t1 = new Triangle(3,4,5);
        Triangle t2 = new Triangle(6,6,6);
        System.out.println(t1);
        System.out.println(t2);

        System.out.println(Math.PI);
        System.out.println(Integer.MAX_VALUE);

        Triangle t3 = new Triangle();
    }
}

4.2 super 关键字

super:代表父类的

前提条件:父类的构造器、方法、成员变量,都必须是非 private

它有 3 种用法:

  • 在子类构造器首行:

    • super(); 代表调用父类的无参构造(默认是它)
    • super(参数); 代表调用父类的有参构造
  • 调用父类的方法

    • super.方法:当子类重写了父类的某个方法,在子类中又想要调用父类被重写的方法时,需要加 super.。如果子类并未重写,那么可以省略 super.
  • 调用父类的成员变量

    • super.成员变量:当子类定义了与父类同名的成员变量时,在子类中想要使用父类的同名的成员变量,需要加 super.。如果没有重名问题,那么可以省略 super.

访问变量时:

(1)不加 this 和 super,从局部开始找

(2)加 this.,不从局部找,从本类的成员变量开始找,找到就停,没找到去父类

(3)加 super.,不从局部和本类找,直接从父类开始找

示例代码 1

java
package com.atguigu.keyword;

public class Base {
    int a = 1;
    int b = 1;
    public void method(){
        System.out.println("父类Base的method方法");
    }
}
java
package com.atguigu.keyword;

public class Sub extends Base{
    int a = 2;
    int c = 2;

    public void printA(){
        System.out.println("a = " + a);
        System.out.println("super.a = " + super.a);
    }
    public void showA(int a){
        System.out.println("a = " + a);//先找局部的
        System.out.println("this.a = " + this.a);//局部变量与成员变量重名时,在成员变量前面加this.
        System.out.println("super.a = " + super.a);//子类成员变量与父类成员变量重名是,在父类的成员变量前面加super.
    }

    public void printB(){
        System.out.println("b = " + b);//先找局部的,找不到再从本类成员变量找,再找不到找父类的
        System.out.println("b = " + this.b);//先找本类成员变量找,找不到找父类的
        System.out.println("super.b = " + super.b);//直接找父类的
    }

    public void printC(){
        System.out.println("c = " + c);
        System.out.println("c = " + this.c);
//        System.out.println("super.c = " + super.c);//错误,因为c不是父类的
    }
    public void method(){
        super.method();
    }
}
java
package com.atguigu.keyword;

public class TestSub {
    public static void main(String[] args) {
        Sub sub = new Sub();
//        sub.method();
//        sub.printA();
        sub.printB();

//        sub.showA(3);
    }
}