JavaSE基础笔记——Javaoo(面向对象进阶:static与继承)

1、Static:

static是什么?

  • 它是静态的意思,可以修饰成员变量和成员方法。
  • static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。

static修饰成员变量的用法:

成员变量可以分为两类:

  • 静态成员变量(有static修饰的,属于类,内存中加载一次):经常表示如在线人数信息等需要被共享的信息,可以被共享访问。
  • 访问:类名.静态成员变量(推荐) / 或 对象.静态成员变量(不推荐)
  • 实例成员变量(无static修饰,存在于每个对象里):常表示名字name、年龄age、等属于每个对象的信息。
  • 访问:对象.实例成员变量

使用user用户类进行代码演示:

public class User {
    /**
     在线人数。
     注意:static修饰的成员变量:静态成员变量,只在内存中有一份,可以被共享
     */
    public static int onlineNumber = 161;
    /**
     实例成员变量:无static修饰,属于每个对象的,必须用对象名,访问
     */
    private String name;
    private int age;

    public static void main(String[] args) {
        //目标: 理解static修饰成员变量的作用和访问特点。
        //1、类名.静态成员变量。(有static修饰,属于类,内存中加载一次):常表示如在线人数信息等
        //需要被共享的信息,可以被共享访问。
        System.out.println(User.onlineNumber);//161

        //2、对象名.实例成员变量。(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。
        //System.out.println(User.name); // 报错
        User u = new User();
        u.name = "张三";
        u.age = 21;
        System.out.println(u.name);//张三
        System.out.println(u.age);//21
        u.onlineNumber++ ; // 新来了一个人
        System.out.println(u.onlineNumber);//162

        User u2 = new User();
        u2.name = "张三";
        u2.age = 21;
        System.out.println(u2.name);//张三
        System.out.println(u2.age);//21
        u.onlineNumber++ ; // 新来了一个人

        System.out.println(u.onlineNumber);//163
        System.out.println(User.onlineNumber); //推荐方式//163
        //注意:同一个类中静态成员变量的访问可以省略类名。
        System.out.println(onlineNumber);//163
    }
}

static修饰成员方法的基本用法:

成员方法的分类:

  • 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
  • 实例成员方法(无static修饰,归属于对象),只能用对象触发访问。

使用学生类的代码演示:

public class Student {
    /**
     实例成员变量:无static修饰,属于对象
     */
    private String name;

    /**
     静态成员方法:有static修饰,归属于类,可以被共享访问,用类名或者对象名都可以访问
     */
    public static int getMax(int age1 , int age2){
        return age1 > age2 ? age1 : age2;
    }

    /**
     实例方法:属于对象的,只能用对象触发访问
     */
    public void study(){
        System.out.println(name + "在好好学习,天天向上-");
    }


    public static void main(String[] args){
        //1、类名.静态成员方法
        System.out.println(Student.getMax(10, 3));//10
        //注意:同一个类中,访问静态方法,类名可以省略不写。
        System.out.println(getMax(10, 32));//32

        //study(); //报错了,一定要拿对象访问。
        //2、对象.实例方法
        Student s = new Student();
        s.name = "猪八戒";
        s.study();

        //3、对象.静态方法(语法是可行,但是不推荐)
        System.out.println(s.getMax(13,34));
    }

}

使用场景:

  • 表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。

  • 如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。

 static的注意事项:

  • 静态方法只能访问静态的成员。不可以直接访问实例成员
  • 实例方法可以访问静态的成员,也可以访问实例成员
  • 静态方法中不可以出现this关键字

代码演示:

public class Test3 {

    /**
     静态成员
     */
    public static int onlineNumber = 10;
    public static void test2(){
        System.out.println("==test2==");
    }

    /**
     实例成员
     */
    private String name;
    public void run(){
        System.out.println(name + "跑得快");
    }

    //3、静态方法中不可以出现this关键字
    public static void test3(){
        //System.out.println(this); //this只能代表当前对象!!静态对象可以不用对象调用
    }

    //2、实例方法可以访问静态的成员,也可以访问实例成员
    public void go(){
        System.out.println(Test3.onlineNumber);//类名访问也可以
        System.out.println(onlineNumber);
        test2();
        System.out.println(name);
        run();
    }

    //1、静态方法只能访问静态的成员。不可以直接访问实例成员
    public static void test(){
        System.out.println(Test3.onlineNumber);//类名访问也可以
        System.out.println(onlineNumber);
        test2();
        //System.out.println(name);//不可以直接访问实例成员
        //run();//不可以直接访问实例成员方法
    }
    public static void main(String[] args) {
        //目标:理解static 访问相关的语法:面试笔试题,或者以后理解程序很重要的知识(拓展)。
    }
}

static应用知识:工具类 

  • 类(内部)里都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的。
  • 好处:一次编写,处处可用,提高代码的重用性。

  • 要求:建议工具类的构造器私有化处理。

案例1:

在企业管理系统里,通常需要一个系统的很多业务处使用验证码进行防刷新等安全控制。

/**
 工具类:内部是一些静态方法。每个方法完成一个功能为目的。
 好处:一次编写,处处可用,提高代码的重用性。
 要求:建议工具类的构造器私有化处理
 */
public final class Util {
    /**
     注意:由于工具类无需创建对象,所以把其构造器私有化会显得很专业
     */
    private Util(){
    }
    /**
     静态方法
     */
    public static String createVerifycode(int n ){
        //开发一个验证码
        //1、定义一个变量记住验证码
        String code = "" ;
        //2、定义一个变量记住全部验证码字符
        String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        //3、定义一个循环生成几个随即索引,去得到几个字符
        Random r = new Random();
        for (int i = 0; i < n; i++) {
            //4、获取随即索引对应的字符,连接给code
            int index = r.nextInt(data.length());
            code += data.charAt(index);
        }
        return code;
    }
}
public class Login {
    public static void main(String[] args) {
        //开发一个验证码
        System.out.println(Util.createVerifycode(6));
        //Util util = new Util();//构造器私有之后不能构建对象。显得更专业。
    }
}

为什么工具类中的方法不用实例方法做?

  • 实例方法需要创建对象调用。
  • 此时用对象只是为了调用方法,这样会浪费内存。

工具类定义时的其他要求:

由于工具类里面都是静态方法,直接使用类名即可访问。因此,工具类无需创建对象,建议将工具类的构造器进行私有。

案例2:

设计一个数组的工具类

/**
 练习:完成数组的工具类的设计
 */
public class ArrayUtils {
    /**
     私有构造器
     */
    private ArrayUtils(){
    }

    /**
     定义工具方法:静态方法
     */
    //toString方法:用于返回整数数组的内容
    public static String toString(int[]arr){
        //1、一些校验
        if (arr == null ){
            return null;
        }

        //2、拼接内容并返回
        String result = "[" ;
        for (int i = 0; i < arr.length; i++) {
            result += (i == arr.length-1 ? arr[i] : arr[i] + ",");
        }
        result += "]";
        return result;
    }
    //getAerage方法:统计平均值(平均值为去掉最低分和最高分后的分值)
    public static double getAweage(int[]arr){
        if (arr.length !=0){
            //1、找出数组中的最高分和最低分
            int max = arr[0];
            int min = arr[0];
            int sum = 0;
            for (int i = 0; i < arr.length; i++) {
                if (max < arr[i]){
                    max = arr[i];
                }
                if (min > arr[i]){
                    min = arr[i];
                }
                //总分
                sum += arr[i];
            }
            //3、求平均值
            double result =  (sum - max - min) *1.0/ (arr.length - 2);
            return  result;
        }
        return 0;
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        int[]arr = null;
        int[]arr1 = {};
        int[]arr2 = {12,23,44,99};

        System.out.println(ArrayUtils.toString(arr));
        System.out.println(ArrayUtils.toString(arr1));
        System.out.println(ArrayUtils.toString(arr2));
        System.out.println(ArrayUtils.getAweage(arr2));
    }
}

 static应用知识:代码块

代码块概述:

  • 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
  • 在Java类下,使用{}括起来的代码被叫做代码块。

代码块分类:

  • 静态代码块

格式:static{}

特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次

使用场景:在类加载的时候做一些静态数据初始化的操作,以方便后续使用。

public class StaticDemo1 {

    public static String schoolName;

    /**
     静态代码块:有static修饰,属于类,与类一起优先加载一次,自动触发执行。
     作用:用于初始化静态资源。
     */
    static {
        System.out.println("-----静态代码块被触发执行了-----");//先运行
        schoolName = "黑马";
    }

    public static void main(String[] args) {
        //目标:先理解静态代码块
        System.out.println("-----main方法执行-----");//后执行
        System.out.println(schoolName);
    }
}
  • 构造代码块(了解,见的少)

格式:{}

特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行。

使用场景:初始化实例资源。

public class StaticDemo2 {

    private String name;

    public StaticDemo2(){
        System.out.println("===无参构造器被触发执行===");//在执行
    }
    /**
     实例代码块:无static修饰,属于对象,每次构建对象时,都会触发一次执行。
     作用:初始化实例资源
     */
    {
        name = "张三";
        System.out.println("=====实例代码块被触发执行=====");//构建对象时,先执行
    }

    public static void main(String[] args) {
        //目标:先理解实例代码块(构造代码块)。
        StaticDemo2 s1 = new StaticDemo2();
        System.out.println(s1.name);//最后执行的

        StaticDemo2 s2 = new StaticDemo2();
        System.out.println(s2.name);
    }
}

练习案例:

斗地主游戏:提前准备好54张牌,后续才可以直接使用这些牌数据。

public class StaticTest3 {

    /**
     1、定义一个静态的集合存储54张牌,这样这个集合只加载一个。因为当前房间只需要一副牌。
     */
    public static ArrayList<String> cards = new ArrayList<>();

    /**
    2、在程序真正运行main方法前,把54张牌放进去吧,后续游戏可以直接使用了。
     */
    static {
        //3、正式做牌,放到集合中去即可。
        //a、定义一个数组存储全部点数:类型确定了,个数确定了。
        String[] sizes = {"3" , "4" , "5" ,"6" , "7" , "8" ,"9" , "10" , "J" ,"Q" , "K" , "A" , "2"};
        //b、定义一个数组存储全部花色:类型确定了,个数确定了。
        String[] colors = {"♥" , "♠" , "♦" , "♣"};
        //c、遍历点数
        for (int i = 0; i < sizes.length; i++) {
            //size[i]
            //d、遍历花色
            for (int j = 0; j < colors.length; j++) {
                //colors[j]
                //一张牌
                String card = sizes[i] + colors[j];
                cards.add(card);
            }
        }
        //e、单独加入大小王。
        cards.add("大🃏");
        cards.add("小🃏");
    }

    public static void main(String[] args) {
        //目标:模拟游戏启动前,初始化54张牌数据。
        System.out.println("新牌:" + cards);
    }
}

 static应用知识:单例设计模式

什么是设计模式(Design pattern)

  • 开发里经常会遇到一些问题,一个问题通常会有n种解决办法,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称为设计模式。
  • 设计模式有20多种,对应20多种软件开发种会遇到的问题。
  • 学设计模式主要学习两点:

                1、这种模式原来解决什么问题。

                2、遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。

单例设计模式:

  • 可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
  • 例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。

单例的实现方式很多。。。。

  • 饿汉单例模式

在用类获取对象的时候,对象已经提前创建好了。

设计步骤:1、定义一个类,把构造器私有。

                2、定义一个静态变量存储一个对象。

/**
 使用饿汉单例实现单例类
 */

public class SingleInstance {
    /**
     2、饿汉单例是在获取对象前,对象已经提前准备好了一个。
     这个对象只能是一个,所以定义静态成员变量记住。
     */
    public static SingleInstance instance = new SingleInstance();

    /**
     1、必须把构造器私有化。
     如果不私有化,可以随意的创建对象,就不能实现单例了
     */
    private SingleInstance(){
    }
}
    public static void main(String[] args) {
        //目标:理解饿汉单例的设计步骤。
        SingleInstance s1 = SingleInstance.instance;
        SingleInstance s2 = SingleInstance.instance;
        System.out.println(s1 == s2 );//因为每次拿对象,拿的是同一个对象,所以返回true。
    }
  • 懒汉单例模式

在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。

设计步骤:1、定义一个类,把构造器私有。

                2、定义一个静态变量存储一个对象即可。不能new对象,new对象的话变成了恶汉单例。

                3、提供一个方法返回单例对象。

/**
 懒汉单例:在真正需要该对象的时候,才去创建一个对象(延迟加载对象)
 */

public class SingleInstance2 {

    /**
     2、定义一个静态的成员变量负责存储一个对象。
     只加载一次,只有一份。
     注意:最好私有化,这也昂可以避免给别人挖坑!
     */
    private static SingleInstance2 instance;

    /**
     3、提供一个方法,对外返回单例对象。
     */
    public static SingleInstance2 getInstance(){
        if (instance == null){
            //第一次来拿对象,此时需要创建对象
            instance = new SingleInstance2();
        }
        //不是空的话,就不能创建对象了。
        return instance;
    }

    /**
     1、构造器私有
     */
    private SingleInstance2(){
    }
}
public class Test2 {
    public static void main(String[] args) {
        //目标:掌握懒汉单例的设计,理解其思想。
        SingleInstance2 s1 = SingleInstance2.getInstance();
        SingleInstance2 s2 = SingleInstance2.getInstance();
        System.out.println(s1 == s2);//true
    }
}

static的案例:

创建一个球员类,该类最多只允许创建11个对象,提示利用static和封装性来完成。

/**
 * 创建一个球员类,并且该类最多只允许创建十一个对象。提示利用 static 和 封装性来完成。
 */
public class Player {
    /**
     * 球员号码
     */
    private int number;
    /**
     * 球员名字
     */
    private String name;
    /**
     * 球员年龄
     */
    private int age;
    /**
     * 球员数量
     */
    private static int count = 0;
    /**
     * 临时变量。最多创建的球员数11
     */
    public static int num = 11;

    /**
     * 构造函数私有化,不允许自己创建对象
     *
     */
    private Player() {
        count++;
    }

    private Player(int number, String name,int age) {
        this.number = number;
        this.name = name;
        this.age = age;
        count++;
    }

    /**
     * 方法1
     * 定义一个判断是否已经创建了11个对象的方法
     * @return count<11时返回创建的对象
      *         >11时返回null
     */
    public static Player CreatePlayer(){
        //count小于11时,可以创建新的对象
        if (count < num){
            return new Player();
        }else{
            //大于11个,就返回null
            return null;
        }
    }

    public static Player CreatePlayer(int number, String name,int age){
        //count小于11时,可以创建新的对象
        if (count < num){
            return new Player(number,name,age);
        }else{
            return null;
        }
    }

    /**
     * 方法2
     * 创建新的对象的方法
     * @return
     */
    public static Player CreatNew(){
        //count小于等于11时,可以创建新的对象
        if (count<num){
            System.out.println("你的第"+ (count + 1) + "个Player对象已经创建");
            return new Player();
        }else{
            System.out.println("创建对象失败,最多创建"+ num +"个");
            return null;
        }
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    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 static int getCount() {
        return count;
    }
}
public class PlayTest {
    public static void main(String[] args) {
        while (true){
            Player player = Player.CreatePlayer();
            if (player!=null){
                System.out.println("创建了第"+ Player.getCount() +"个运动员对象");
            }else{
                System.out.println("已经创建了"+ Player.num +"个对象,不能再创建了。");
                break;
            }
        }

        /*while (true){
            Player player = Player.CreatNew();
            if (player == null){
                break;
            }
        }*/
    }
}

复习:static——静态

凡是由static修饰的东西,都具有两个共同特点:

1、与对象无关,与类有关;

2、会在类加载期有特殊动作

static属性

普通属性再内存里存放在每一个对象身上的。也就是说:当我们new一个对象,在这个对象的存放空间里就会声明一个普通属性;new多个对象,每个对象的存放空间里面各有一个该属性。所以,我们操作哪个对象就操作该对象身上的该属性,而不会影响其他对象。所以这种属性又叫做“成员属性”,因为它是组成对象的一部分。

static属性:它不是存在于每个对象身上的,而是全类共享一个值。该属性是在类加载期就把这个属性产生了,并放在内存中的静态区。之后无论在代码里面我们用对象访问该属性,还是用类名访问该属性,都是指向同一个空间。

static方法

普通方法:是与对象相关的

这个相关体现在两个地方:

1、该方法的调用,必须用对象.的方式;

2、该方法的实现部分,能够操作到该对象的普通属性或普通方法(关键)。当然静态属性和静态方法也可以。

static方法:是与对象无关的,同样体现在2处:

1、该方法的调用,可以使用类名.的方式,且用类名.和用对象.没有差异;

2、该方法的实现部分,只能调用到该方法的静态属性和静态方法。

这两个差异的理解:1、从场景上看,既然这个方法是静态的,也就是说它的执行与对象无关,那么在它内部也就不应该知道到底在操作哪个当前对象,也就不能确定这个对象相关的属性和方法;

2、从底层实现看,静态方法在加载期是被优先加载的,也就是说在加载静态方法的时候,还没有加载这个类里面的非静态内容,而如果这个时候静态方法里面调用了非静态内容,JVM会不认识,所以编译报错。

我们在自己写代码时要注意,只有工具类的工具方法才能写出static

2、继承(面向对象三大特征之二):

有些人把抽象也算作对象的第4个特点。

什么是继承?

让拥有is-a关系的类能够共享属性和行为,达到代码的共享。

Java中提供一个关键字extemds,使用这个关键字,我们可以让一个类和另一个类建立起父子关系。

如:public class Student extends People{}

Student称为子类(派生类),People称为父类(基类或超类)。

作用:

当子类继承父类后,就可以直接使用父类公共的属性和方法了
Java中子类更强大。

继承的优点和缺点

优点 :

  1. 可以提高代码的复用性,减少代码冗余,增强类的功能扩展性。

  2. 子类可以扩展自己的属性和方法

  3. 子类可以使用父类的非 private的属性和方法.

  4. 代码的设计更加的简单

缺点:

  1. 增加了耦合性(两个类的关联度)

  2. 继承可以打破封装

继承设计规范:

子类们相同特性(共同属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义

在子类自己里。
原因:如果子类的独有属性、行为定义在父类中,会导致其他子类也会得到这些属性和行为,不符合面向对象逻辑。

代码演示:

人类:

/**
 人类:作为父类
 */

public class People {
    public void run(){
        System.out.println("人会跑---");
    }
}

学生类:

/**
 学生类:作为子类
 */
public class Student extends People{
}

测试类:

public class Test {
    public static void main(String[] args) {
        // 目标:认识继承的这种关系。搞清楚使用继承的好处。
        Student s = new Student();
        s.run();
    }
}

继承的设计规范案例:

教学管理系统中,存在学生、老师角色会进入系统。

分析:

学生信息和行为(名称、年龄、所在班级、查看课表、填写听课反馈)

老师信息和行为(名称、年龄、所在部门名称、查看课表、发布问题)

定义人类最为父类包含属性(名称、年龄),行为(查看课表

/**
 父类
 */

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

    /**
     查看课表
     */
    public void queryCourse(){
        System.out.println(name + "在查看课表---");
    }

    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 class Student extends People{
    /**
     独有的信息:所在班级
     */
    private String class1;

    public String getClass1() {
        return class1;
    }

    public void setClass1(String class1) {
        this.class1 = class1;
    }

    /**
     独有的行为,填写反馈信息
     */
    public void writeInfo(){
        System.out.println(getName() + "写下了,学习语法,好哈皮---");
    }
}

定义子类:老师类类包含属性(所在部门名称),行为(发布问题

/**
 子类
 */

public class Teacher extends People {
    /**
     独有的信息:所在部门
     */
    private String department;

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    /**
     独有的行为:发布问题
     */
    public void postQuestions(){
        System.out.println(getName() + "发布了----问题");
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //目标:理解继承的设计思想。
        Student s = new Student();
        s.setName("蜘蛛精");//使用父类的
        s.setAge(999);//使用父类的
        s.setClass1("通信1804班");
        System.out.println(s.getName());//使用父类的
        System.out.println(s.getAge());//使用父类的
        System.out.println(s.getClass1());//使用子类的
        s.queryCourse();//父类的
        s.writeInfo();//子类的方法
        Teacher t = new Teacher();
        t.setName("张三");//父类的
        t.setAge(23);//父类的
        t.setDepartment("语文部");
        System.out.println(t.getName());//父类的
        System.out.println(t.getAge());//父类的
        System.out.println(t.getDepartment());//子类自己的
        t.queryCourse();//父类的
        t.postQuestions();//子类的方法
    }
}

继承的特点:

  1. 子类不能继承父类的构造器,可以继承父类的属性和行为。
  2. java是单继承模式:一个类只能继承一个直接父亲
  3. java只支持单继承(子类只能继承一个直接父类),不支持多继承(子类不能同时继承多个父类),但是支持多层继承(子类A继承父类B,父类B可以继承父类C)。
  4. java中所有的类都是object类的子类

单继承:

在面向对象的概念里,是没有单继承还是多继承这种说法的。只是说Java语言在设计它的实现的时候,采用了单继承;也有别的编程语言采用了多继承。

多继承的好处:丰富度。

坏处:复杂度。我们在复杂情况下设计出来的类结构图一定会呈现出网状结构。

单继承的好处:无论多复杂的情况,设计出来的类层次结构一定是简单的树状结构。

坏处:丢失了丰富度。所以也就有了接口的多实现来进行补充。

object类特点:

java所有类,要么直接继承了object,要么默认继承了object,要么间接继承了object,object类是祖宗类。

有一句话: 少继承 多实现。

所有的类和数组都继承于这个类,它是根也是祖宗。它的属性和行为是所有类都共有的。也就是说先人在设计的时候是把共用性最强的行为写在它当中了。所以这个类是唯一一个我们要把它所有方法都要进行掌握的一个类。

在目前这个阶段至少要掌握两个方法的使用和知道一个方法:

1、equals()——经常重写

作用:判断两个对象是否相等。

那么和”==“号有区别吗?

==是用来比较两个对象引用是否是同一个,或者说两个引用是否指向同一个对象。

equals是用来比较两个对象的内容是否相等。

只是由于原生的equals方法是定义在Object当中的,所以它不知道子类有哪些内容,所以在Object当中的实现只能是返回”==“号比较的结果。而我们通过继承,子类会自动具备来自于Object的这个方法,然后通过重写我们自己定义比较规则。

2、toString()

作用:返回对象的字符串描述。

原生的toString方法是定义在Object当中的,同样不知道子类有哪些内容,当然也就没有办法确认子类对象需要的字符串描述,所以只能采用统一的”类型@对象的引用值“

我们自己写的类需要通过重写,然后返回一个我们自定义的字符串。

另外toString方法在对象做字符串操作的时候会被自动调用,无需我们显式调用

3、finallize()

在JDK9之后过时了

作用:在JDK9之前用来销毁对象的方法。我们可以把它和构造方法看成一对。由于Java出于安全性的考虑,不让我们操作对象的销毁,所以它把销毁方法写好,放在Object里,然后所有的类和数组通过继承都拥有了销毁方法。我们都知道Java的对象回收都是由GC(垃圾回收机制)来进行操作的,而GC正是通过调用这个方法来完成对象的销毁。

记住:finallize方法是定义在Object当中的,由GC调用。

这个方法通常不重写,因为你不知道如何重写。如果有需要重写,那么在写完自己的实现后调用一下super.finalize()完成父类的实现。

4、重写——子类把父类的方法给重写实现

重写的规范:方法名必须一致;

参数列表必须一致(包括:参数的个数,参数的类型和参数的顺序,没有参数名);

返回类型可以变的,但是重写之后只能返回重写前的返回类型或它的子类型;

访问修饰符也可以变,但是子类重写后的访问修饰符只能往大的方向变或不变。

public class Test {
    public static void main(String[] args) {
        //目标:理解继承的特点
        //1、子类不能继承父类的构造器,可以继承父类的属性和行为。
        //2、java是单继承模式:一个类只能继承一个直接父亲
        //3、java不支持多继承,但是支持多层继承。
        //4、java中所有的类都是object类的子类
        //object类特点:java所有类,要么直接继承了object,要么默认继承了object,要么间接继承了object,object类是祖宗类。
        //子类是否可以继承父类的私有成员?可以的,只是不能直接访问。
        Tiger t = new Tiger();
        //t.eat();
        //子类是否可以继承父类的静态成员? 我认为不算继承的。只是共享的。
        System.out.println(Tiger.location);
    }
}

/**
 * 父类
 */
class Animal{
    private void eat(){
        System.out.println("动物要吃东西---");
    }

    public static String location = "长隆动物园";
}

/**
 * 子类
 */
class Tiger extends Animal{
}

 继承后:成员变量、成员方法的访问特点

在子类方法中访问成员(成员变量、成员方法)满足:就近原则

就近原则:子类(局部)没有找子类(成员),子类(成员)没有找父类,父类没有就报

错。
如果子父类中出现了重名的成员,此时如果一定要在子类中使用父类怎么办?
格式:super.父类成员变量/父类成员方法

public class Test {
    public static void main(String[] args) {
        //目标:理解继承后成员的访问特点:就近原则。
        
        Dog d = new Dog();
        d.run();//狗跑的贼快---。子类的
        d.lookDoor();//狗可以看门---。子类的
        d.showName();

    }
}

class Animal{
    public String name = "动物名";

    public void run(){
        System.out.println("动物可以跑---");
    }
}

class Dog extends Animal{
    public String name = "狗名";

    public void lookDoor(){
        System.out.println("狗可以看门---");
    }

    public void showName(){
        String name = "局部名";
        System.out.println(name);//局部名
        System.out.println(this.name);//当前子类对象的name(狗名)
        System.out.println(super.name);//找父类的name(动物名)

        super.run();//找父类方法(动物可以跑---)
        run();//子类的run(狗跑的贼快---)
    }

    public void run(){
        System.out.println("狗跑的贼快---");
    }
}

 继承后:方法重写

在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的

方法。

什么情况下需要进行方法的重写?

当子类的实现方式跟父类的实现方式不一样的时候,就需要对父类的方法进行重写( 覆盖).

当子类重写了父类的方法后,则会覆盖父类原来方法的实现。

方法重写应用场景:

当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。;子类可以重写父类中

的方法。

@Override重写注解

1、重写校验注解,加上这个,这个方法必须是正确重写的,这样更安全。

2、提高程序的可读性,代码优雅!

加上该注解后如果重写错误,编译阶段会出现错误提示。

方法重写注意事项和要求:

1、重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。

2、私有方法不能被重写。
3、子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省<protected<public)
4、子类不能重写父类的静态方法,如果重写会报错的。

重写和重载的区别

重载: 方法必须在一个类中,方法名相同, 但是参数列表不相同.

重写: 两个类,并且两个类之间必须有继承或者实现关系. 方法的访问修饰符必须不小于父类的反问修饰符。

方法的返回类型(必须要小于或等于父类的返回类型。前提是引用数据类型) 方法的名字要一样,甚至,方法的参数列表必须要一样。

案例:

旧手机的功能只能是基本的打电话,发信息

新手机的功能:基本的打电话下支持视频通话。基本的发信息下支持发送语音和图片。

public class Test {
    public static void main(String[] args) {
        //目标:认识方法重写。:在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
        //方法重写应用场景:当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。;子类可以重写父类中的方法。
        NewPhone hw = new NewPhone();
        hw.call();
        hw.sendMsg();
    }
}

/**
 新手机:子类
 */
class NewPhone extends Phone{
    //重写的方法
    @Override //1、重写校验注解,加上这个,这个方法必须是正确重写的,这样更安全。2、提高程序的可读性,代码优雅!
   //加上该注解后如果重写错误,编译阶段会出现错误提示
    //方法重写注意事项和要求:1、重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。2、私有方法不能被重写。
    //3、子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省<protected<public)
    //4、子类不能重写父类的静态方法,如果重写会报错的。
    public void call(){
        super.call(); //先用它爸爸的基本功能
        System.out.println("开始视频通话--");
    }
    //重写的方法
    @Override
    public void sendMsg(){
        super.sendMsg();//先用它爸爸的基本功能
        System.out.println("发送有趣的图片--");
    }
    //注意:静态方法不能被重写。
//    @Override
//    public static void test(){
//    }

}

/**
 旧手机:父类的
 */
class Phone{
    public void call(){
        System.out.println("打电话-");
    }

    public void sendMsg(){
        System.out.println("发短信-");
    }
    public static void test(){

    }
}

 继承后:子类构造器的特点

子类的全部构造器默认会先访问父类的无参数构造器,再执行自己。

父类:

public class Animal {
    public Animal(){
        System.out.println("父类Animal无参数构造器被执行-");
    }
}

子类:

public class Dog extends Animal{
    public Dog(){
        super();//写不写都有,默认找父类的无参数构造器执行。
        System.out.println("子类Dog无参数构造器被执行-");
    }

    public Dog(String name){
        super();//写不写都有,默认找父类的无参数构造器执行。
        System.out.println("子类Dog有参数构造器被执行-");
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //目标:认识继承后子类构造器的特点
        //特点:子类的全部构造器默认会先访问父类的无参数构造器,再执行自己
        //原因:1、子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
        //2、子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
        //怎么调用父类构造器的?子类构造器的第一行语句默认都是:super(),不写也存在。

        Dog d1 = new Dog();
        System.out.println(d1);

        System.out.println("------------------");
        Dog d2 = new Dog("金毛");
        System.out.println(d2);
        /*
        父类Animal无参数构造器被执行-
        子类Dog无参数构造器被执行-
        com.itheima.d10_extends_constructor.Dog@1b6d3586
        -----------------
        父类Animal无参数构造器被执行-
        子类Dog有参数构造器被执行-
        com.itheima.d10_extends_constructor.Dog@4554617c
         */
    }
}

继承后:子类构造器访问父类有参构造器

super调用父类有参数构造器的作用:

  • 初始化继承自父类的数据。

作用:通过调用父类有参数构造器来初始化继承自父类的数据。

父类:

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

    public People() {
    }

    public People(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 class Teacher extends People{
    public Teacher(String name , int age){
        //调用父类的有参数构造器,初始化继承自父类的数据
        super(name,age);
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //目标:学习子类构造器如何去访问父类有参数构造器,还要清楚其作用。
        //super调用父类构造器
        //作用:通过调用父类有参数构造器来初始化继承自父类的数据。
        Teacher t = new Teacher("张三",18);
        System.out.println(t.getName());
        System.out.println(t.getAge());
    }
}

 this和super总结

this:代表本类对象的引用;super(超类或者父类 ):代表对父类的引用。

this的总结在这个链接里的第六点:
JavaSE基础笔记——Javaoo(面向对象)_小曹爱编程!的博客-CSDN博客JavaSE基础笔记——Javaoo(面向对象)https://blog.csdn.net/weixin_62993347/article/details/125825317?spm=1001.2014.3001.5501

super 有3中用法:

  1. super.属性 : 调用父类对象的属性(变量)

  2. super.成员方法(参数列表) : 调用父类的某个成员方法

  3. super(参数列表) : 调用父类的某个构造函数

当子类被创建时. 被自动的调用父类的 空构造函数 super( )

this(...)和super(...)使用注意点:

1、子类通过this(...)去调用本类的其他构造器,本类其他构造器会通过super去手动调节父类的构造器,最终还是会调用父类构造器的。
2、注意:this(。。。)super(。。。)都只能放在构造器的第一行,所以二者不能共存在同一个构造器里。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值