static&继承

今日内容

  • Static

  • ==继承==

目录

今日内容

0. 面向对象导学

0.1 知识特点

0.2 学习方式

0.3 重点

1. static

1.0 面向对象知识回顾

1.1 static概述

1.2 修饰成员变量

1.3 修饰成员方法

==1.4 静态使用场景==

1.5 代码块

==2. 继承入门==

2.1 快速入门

2.2 优势和弊端

2.3 注意事项

==3. 方法重写==

3.1 访问权限修饰符

3.2 方法重载(回顾)

==3.3 方法重写==

==4. 继承中成员访问特点==

==4.1 成员变量/方法==

4.2 构造方法


0. 面向对象导学

0.1 知识特点

  • 概念多

  • 重理解

  • 代码少

0.2 学习方式

  • 面向对象之前,要多练,找手感,巩固知识

  • 面向对象相关知识,要多理解

    • 理解的基础上练习

      Java是源于生活的,对比生活理解Java中面向对象思想

    • 重点在理解,代码量很少、

      慢慢理解,不要有压力;慢慢练习体验,等待顿悟的那一刻

0.3 重点

  • 以对象为核心,设计并使用对象保存、处理数据,并最终解决各种问题。

1. static

1.0 面向对象知识回顾

  • 概念

    :对生活中具有相同属性和行为的一类事物的描述/抽象

    对象:某一类别下某个具体的个体

    两者关系:类是对象的描述,对象是类的实体

    原则:Java中万物皆对象

    成员变量:属性,参数。

    1. 独属于某个对象,不与同类下其他对象共享。

    2. 只能通过对象名.成员变量名调用

    成员变量代码演示

    • 定义成员变量

      public class Student{
          String school;
          String name;
          int age;
      }
    • 使用成员变量

      public class StudentDemo{
          public static void main(String[] args){
              Student stu1 = new Student();
              stu1.name="张三";
              
              Student stu2 = new Student();
              stu2.name="李四";
              
              System.out.println(stu1.name);  // 张三。只能是张三
              System.out.println(stu2.name);  // 李四。只能是李四
              
          }
      }
  • 成员方法:功能、行为

    • 只能通过对象名.成员方法名调用

    成员变量代码演示

    • 定义成员方法

      public class Student{
          public void study(String subjectName){
              System.out.println("在学习"+subjectName+"的知识");
          }
      }
    • 使用成员方法

      public class StudentDemo{
          public static void main(String[] args){
              Student stu1 = new Student();
              stu1.study("Java"); // 在学习Java的知识
          }
      }
  • 封装

    面向对象三大特征之一(封装、继承、多态)

    封装:隐藏具体实现细节,对外暴露公共的访问方式

    封装代码演示

    • 定义成员方法

      public class Student{
          
          // 隐藏具体实现细节
          private String name;
          private int age;
          
          // 对外暴露公共的访问方式
          // getter & setter
          
          // name getter
          public String getName(){
              return this.name;
          }
          
          // name setter
          public void setName(String name){
              this.name = name;
          }
          
          // 构造方法(方法名必须与类名一致、没有返回值类型)
          public Student(){}
          
          public Student(String name, int age){
              this.name = name;
              this.age = age;
          }
          
      }
    • 使用构造方法&getter/setter

      public class StudentDemo{
          public static void main(String[] args){
              // 使用构造方法创建对象
              Student stu1 = new Student();
              
              // 使用成员方法:setter
              stu1.setName("夏洛特");
              
              // 使用成员方法:getter
              String name = stu1.getName();
              System.out.println(name);
              
          }
      }

1.1 static概述

static,Java中的关键字,意为静态、全局。

作为修饰符可以修饰类中成员(成员变量、成员方法等)

但是其思想有悖于面向对象,故在Java中使用频率不高。

1.2 修饰成员变量

1.2.1 概述

  • 概念

    被static修饰的成员变量称为类变量

    未被static修饰的成员变量称为实例变量(对象变量)

    为了方便称呼,一般称类变量为静态变量,称实例变量为成员变量

  • 特点

    成员变量独属于某个对象。

    静态变量不再独属于某一个对象,而是属于整个类,被该类下所有对象所共享。

1.2.2 定义格式

static 数据类型 变量名;

  • 举例:

    static String school;

1.2.3 使用格式

方式1:类名.静态变量名

方式2:对象名.静态变量名

注意:

  • 推荐使用方式1

  • 举例

    // 方式1
    Student.school = "杭州黑马";
    ​
    // 方式2
    Student stu = new Student();
    stu.school = "传智中职";

1.2.4 演示代码

  • StaticDemo.java

    class Student {
        // 类变量
        static String school;
    ​
        // 实例变量(对象变量)
        String name;
    }
    public class Test {
        public static void main(String[] args) {
            // 1. 类变量用法
            // 用法1:类名.类变量名(推荐用法)
            Student.school = "杭州黑马";
    ​
            // 2. 创建多个对象分别为类变量赋值
            // 用法2:对象名.类变量名(不推荐)
            Student s1 = new Student();
            s1.school = "传智中职";
    ​
            Student s2 = new Student();
            s2.school = "传智大学";
    ​
            // 3. 通过对象和类分别查看类变量的值
            System.out.println("s1.school = " + s1.school); // 传智大学
            System.out.println("Student.school = " + Student.school); //传智大学
    ​
            // 4. 实例变量的用法:对象.实例变量名
            s1.name = "马春梅";
            s2.name = "秋雅";
    ​
            System.out.println("s1.name = " + s1.name);
            // System.out.println("Student.name = " + Student.name); 报错
        }
    }

  • 图示

1.2.5 内存图示运行原理

1.2.6 定义和使用总结

  • 成员变量属于某个对象。只能通过对象名.成员变量调用

  • 静态变量不再独属于某一个对象,而是属于整个类,被该类下所有对象所共享。

    可以通过对象名.静态变量调用,但是推荐使用类名.静态变量调用

  • 静态变量随着类的加载而初始化,初始化时机早于该类的对象(并且只在加载class文件的时候创建一次)

1.2.7 应用场景

  • 如果一个类的某个属性值只有一种可能性,希望能够被该类下所有对象共享(访问、获取、修改),可以将其定义为静态变量。

  • 但是静态有悖于面向对象思想,所以使用场景较少

  • 常见场景:全局计数器/单例模式(需要配合静态方法)。

全局计数器需求:

  • 系统启动后,要求User类可以记录自己创建了多少个用户对象。

分析:

  • 要记录的数量会变化,所以需要使用变量定义。

  • 要求User自己记录,需要定义为成员变量

  • 要求记录User类有多少个用户对象,肯定不能使用成员变量(依赖于某个User对象)记录,所以要使用静态成员变量。

  • 创建User对象的时候必然要调用构造方法,所以要在构造方法中完成数量的累加

思路:

  1. 定义User类

  2. User类中定义静态变量number(初始值为0)

  3. User类的构造方法中number+1

  4. 定义测试类UserDemo,并编写main方法,

  5. mian方法中创建多个User对象,后获取并输出User的number值

代码:

  • User.java

    package com.itheima.static03;
    ​
    /**
     * 用户实体类
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 11:44
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 用户实体类
     */
    public class User {
    ​
        // 2. User类中定义静态变量,记录创建的对象个数
        static int number;
    ​
        // 3. User类的构造方法中number+1
        public User() {
            number++;
        }
    ​
    }
    ​
  • UserDemo.java

    package com.itheima.static03;
    ​
    // 4.定义测试类UserDemo,并编写main方法
    ​
    /**
     * 用户创建测试类
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 11:49
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 用户创建测试类
     */
    public class UserDemo {
    ​
        public static void main(String[] args) {
            // 5. main方法中创建多个User对象,后获取并输出User的number值
    ​
            User user1 = new User();
            User user2 = new User();
            User user3 = new User();
            User user4 = new User();
    ​
            System.out.println("User.number = " + User.number);
            System.out.println(user1.number);
    ​
        }
    }
  • 运行main方法后,输出结果:

    // 系统创建的User对象个数:4

1.3 修饰成员方法

1.3.1 概述

  • 概念

    被static修饰的成员方法称为类方法

    未被static修饰的成员方法称为实例方法(对象方法)

    为了方便称呼,一般称类方法为静态方法,称实例方法为成员方法

  • 特点(按照调用的方式讨论归属)

    成员方法属于对象;只能通过对象调用;

    静态方法属于类,被该类下所有对象共享;可以通过对象调用,也可以并推荐通过类调用;

1.3.2 定义格式

public static 返回值数据类型 方法名(形参列表){ 方法体 }

  • 举例:

    public static void study(){
        System.out.println("在杭州黑马学习Java");
    }

1.3.3 使用格式

方式1:类名.静态方法名

方式2:对象名.静态方法名

注意:

  • 推荐使用方式1

举例

// 方式1
Student.study();
​
// 方式2
Student stu = new Student();
stu.study();

1.3.4 演示代码

  • 步骤

    1. 定义一个Student类

    2. 在类中定义一个成员变量,分数score

    3. 在类中定义一个静态方法printHelloWorld,内部打印一句话

    4. 定义一个非静态成员方法printPass,根据分数判断是否及格

    5. 定义一个测试类StaticMethodDemo,其中定义main方法

    6. main方法中分别调用静态方法

      6.1 使用Student类直接调用静态方法

      6.2 使用Student类的对象调用静态方法

    7. 调用非静态成员方法,注意两者调用方式的区别。

  • Student.java

    package com.itheima.static04;
    ​
    /**
     * @Author Vsunks.v
     * @Date 2023/2/17 12:04
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    // 1. 定义一个Student类,
    public class Student {
    ​
        // 2. 在类中定义一个成员变量,分数score
        double score;
    ​
        // 3. 在类中定义一个静态方法printHelloWorld
        public static void printHelloWorld() {
            System.out.println("Hello World!");
            System.out.println("Hello World!");
        }
    ​
        // 4. 定义一个非静态成员方法printPass
        public void printPass() {
           /*  if (score >= 60) {
                System.out.println("成绩及格");
            } else {
                System.out.println("成绩不及格");
            } */
    ​
            System.out.println(score >= 60 ? "成绩及格" : "成绩不及格");
        }
    }
    ​
  • StaticMethodDemo.java

    package com.itheima.static04;
    ​
    /**
     * @Author Vsunks.v
     * @Date 2023/2/17 12:08
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    // 5. 定义一个测试类StaticMethodDemo,其中定义main方法
    public class StaticMethodDemo {
        public static void main(String[] args) {
    ​
    ​
            // 6. main方法中分别调用静态方法
            // 6.1 使用Student类直接调用静态方法,推荐方式。
            Student.printHelloWorld();
    ​
            // 6.2 使用Student类的对象调用静态方法,不推荐的方式。
            Student stu = new Student();
            stu.printHelloWorld();
    ​
            // 7. 调用非静态成员方法,注意两者调用方式的区别。
            stu.printPass();
            stu.score = 60;
            stu.printPass();
    ​
            // Student.printPass();    // 报错。成员方法,只能通过对象名调用,不能通过类名直接调用
    ​
        }
    }

1.3.5 内存图示运行原理

1.3.6 注意事项

  • 静态方法中,只能访问静态成员(静态变量,静态方法)

  • 非静态方法中,可以使用静态成员,也可以使用非静态成员

  • 静态方法中,没有this关键字;成员方法中可以使用this

    this:当前对象的引用(之后会讲) this需要在创建对象之后,才会存在,静态方法调用的时候,对象可能还没有被创建

总结:静态只能访问静态

演示代码:

  • StaticDemo02.java

    package com.itheima.static04;
    ​
    /**
     * 演示Static的的注意事项
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 15:42
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 演示Static的的注意事项
     */
    public class StaticDemo {
    ​
        /**
         * 总结:静态只能访问静态
         */
    ​
        // 定义静态变量
        static String school;
    ​
        // 定义非静态变量
        String name;
    ​
        // 定义静态方法1
        // 静态方法中,只能访问静态成员(静态变量,静态方法)
        public static void printHelloWorld() {
            // 静态方法中,只能访问静态成员:静态变量
            StaticDemo.school = "杭州黑马";
            // 使用的是本类中的(静态)成员,类名可以省略不写
            school = "传智中职";
            // 静态方法中,只能访问静态成员:静态方法
            printHelloWorld2();
    ​
            // 静态方法中,能否访问非静态成员
            // Non-static field 'name' cannot be referenced from a static context
            // 非静态的成员变量name不能在静态的上下文中引用
            // name = "都行";
            // com.itheima.static04.StaticDemo.this' cannot be referenced from a static context
            // static方法中不能使用this
            // this.name = "都行";
    ​
            // Non-static method 'printPass()' cannot be referenced from a static context
            // 非静态的成员方法printPass()不能在静态的上下文中引用
            // printPass();
    ​
    ​
        }
    ​
        // 定义静态方法2
        public static void printHelloWorld2() {
    ​
        }
    ​
        // 定义成员方法1
        // 非静态方法中,可以使用静态成员,也可以使用非静态成员
        public void printPass() {
            // 访问静态成员
            this.school = "传智大学";
            printHelloWorld2();
    ​
            // 访问非静态成员
            name = "buzhidao";
            printPass2();
    ​
        }
    ​
        // 定义成员方法2
        public void printPass2() {
    ​
        }
    ​
    }

思考

  1. main方法是什么方法

    // static修饰的静态方法
    public static void main(String[] args){}
  2. 在学习方法时,为什么自定义的方法都要被static修饰

    /**
        定义后方法之后,我们需要调用测试,以便看到效果。
        基于当时已学内容,只能在main方法中调用测试
        
        基于静态只能调用静态的原则
        被main方法调用的其他自定义方法,只能被static修饰
    */    

1.3.7 应用场景

  • 静态方法可以直接使用类调用,不再依赖于对象,有悖于面向对象思想,所以使用场景较少

  • 常见场景:工具类/单例模式(需要配合静态变量)。

==1.4 静态使用场景==

==1.4.1 场景1:工具类==

工具类概念:

  • 一个类,内含可以轻松实现各种功能的方法

  • 类中方法调用要简单方便,才可以称之为工具

总结:工具类中的方法多为静态方法,可以通过类名直接调用,方便使用。

验证码案例需求:

  • 定义一个工具类,类中需要提供一个方法,该方法可以生成一个指定长度的验证码

  • 验证码中字符可以包含:数字、小写字母、大写字母

分析:

  • 工具类中方法多为静态,需要定义一个类,类中定义静态方法

  • 验证码中字符是随机的,可以考虑配合随机数生成器使用

  • 随机数得到的是一个随机的数字,和数字相关的概念有索引、个数、长度等

  • 可以通过字符串的方法charAt()方法,获取指定索引处的字符

  • 因为长度不确定,需要向生成验证码的方法传参以指定生成随机数的个数,也就是随机获取字符的个数

  • 随机数生成器 + 字符串 + 索引,完成需求

思路:

  1. 定义工具类VeryfyCodeUtil

  2. VeryfyCode类中定义静态方法generateCode

    方法返回值为验证码,String类型

    方法参数为验证码的长度n,int类型

  3. 方法内部 定义一个变量code,用以记录生成的验证码

  4. 方法内部 定义一个由所有可选字符组成的字符串data[0-9a-zA-Z]

  5. 方法内部 创建Random对象,用于生成随机数

  6. 方法内部 编写for循环,循环次数为n

  7. 循环体中,使用随机数生成器生成一个[0,data.length),并将其作为索引从data中获取该索引处的字符,最后将字符拼接进验证码变量code中

  8. 方法中循环外,返回生成好的验证码code

  9. 定义测试类LoginDemo/RegisterDemo,在其main方法中测试工具类生成验证码

    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ"

代码:

  • VeryfyCodeUtil.java

    package com.itheima.static05;
    ​
    import java.util.Random;
    ​
    /**
     * 验证码生成的工具类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 验证码生成的工具类
     */
    // 1. 定义工具类VeryfyCodeUtil
    public class VeryfyCodeUtil {
    ​
        /**
         * 2.VeryfyCode类中定义静态方法generateCode
         * <p>
         * 方法返回值为验证码,String类型
         * <p>
         * 方法参数为验证码的长度n,int类型
         *
         * @param length
         * @return
         */
        public static String generateCode(int length) {
    ​
            // 3. 定义一个变量code,用以记录生成的验证码
            String code = "";
    ​
            // 4. 定义一个由所有可选字符组成的字符串data[0-9a-zA-Z]
            String data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ";
    ​
            // 5. 创建Random对象,用于生成随机数
            Random r = new Random();
    ​
            // 6. 编写for循环,循环次数为n
            for (int i = 0; i < length; i++) {
                // 7.1使用随机数生成器生成一个[0,data.length)
                int index = r.nextInt(data.length()); // 生成[0,data.length())
    ​
                // 7.2 将其作为索引从data中获取该索引处的字符
                char c = data.charAt(index);
    ​
                // 7.3 将字符拼接进验证码变量code中
                code += c;
            }
    ​
    ​
            // 8. 方法中循环外,返回生成好的验证码code
            return code;
        }
    }
  • RegisterDemo.java

    //模拟注册界面的验证码
    public class registerDemo{
        public static void main(String[] args){
            // 通过类名直接调用,推荐用法
            System.out.println(VeryfyCodeUtil.generateCode(4));
        }
    }

  • LoginDemo.java

    //模拟登录界面的验证码
    public class LoginDemo{
        public static void main(String[] args){
            // 也可以通过对象名调用,不推荐
            System.out.println(new VeryfyCodeUtil().generateCode(4));
        }
    }

  • 运行main方法后,输出结果:

    /**  每次都不一样
        注册验证码:kMcnGP
        登录验证码:dOll
    */

补充:

  • 工具类中的方法基本上都是静态的,所以都可以通过类名调用,这时候就不需要创建该类的对象

  • 为了防止使用者创建该类的对象,我们可以把为工具类添加唯一的一个私有的构造方法。

    public class VeryfyCodeUtil{
        
        // 私有化唯一的构造方法:这样该类外面就不能使用构造方法new对象了
        private VeryfyCodeUtil(){}    
        
        // 静态方法,可以直接通过类名调用
        public static String generateCode(int n){
            // 1.定义一个字符串,用来记录产生的验证码
            String code = "";
            
            // 2.验证码是由所有的大写字母、小写字母、者数字字符组成
            // 这里先把所有的字符存入一个字符串,一会从字符串中随机找字符
            String data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ";
            
            //3.循环n次,产生n个索引,再通过索引获取字符
            Random r = new Random();
            for(int i=0; i<n; i++){
                int index = r.nextInt(data.length());
                char ch = data.charAt(index);
                //4.把获取到的字符,拼接到code验证码字符串上。
                code+=ch;
            }
            
            //最后返回code,code的值就是验证码
            return code;
        }
    }
  • LoginDemo.java

    //模拟登录界面的验证码
    public class LoginDemo{
        public static void main(String[] args){
            // 工具类构造方法被私有化,外界无法调用其私有构造方法,创建其对象
            // System.out.println(new VeryfyCodeUtil().generateCode(4));
            
            // 只能通过类名调用
            System.out.println(VeryfyCodeUtil.generateCode(4));
        }
    }

==1.4.2 场景2:单例设计模式==

a. 概念:

  • 单例:单个实例(对象)。在程序运行期间,某个类的实例最多只能有一个。

单例模式是常见的设计模式之一。

  • 设计模式:套路、解决方案。

    编程实践中,会遇到各种问题。

    一个问题通常有n种解法,在特定场景下,其中一种解法是最优的,最优解被总结出来,称之为设计模式

  • 常见的设计模式有20多种,也就是编程实践中20多种常见问题的解决方案/套路。

  • 单例模式就是要保证某个类的对象最多只能有一个的解决方案/套路。

a.学习设计模式,也就是要学两点

  • 这个设计模式能解决什么问题?

  • 怎么写?

b. 为什么要单例

  • 只创建一个对象,避免频繁大量创建对象,浪费内存空间/CPU等硬件性能

  • 业务要求只能有一个

c. 单例的实现方式:

包括但不仅限于以下方式:

  • 饿汉式

  • 懒汉式

  • 枚举

  • 静态内部类

单例的实现方式有多种,结合目前所学内容,演示饿汉式。其他方式之后有机会再学习。

d. 步骤:

  1. 定义类,并私有化构造方法

  2. 类中定义一个私有的自己类型的静态变量,并初始化

  3. 提供一个公共的静态方法,方法中返回静态变量

e. 演示代码

  • SingletonDemo.java

     // 单例类
    public class SingletonDemo{
        // 2、定义一个静态变量记住类的一个对象
        private static SingletonDemo instance = new SingletonDemo();
    ​
        // 1、私有构造器
        private SingletonDemo(){    }
    ​
        // 3、定义一个静态方法返回对象
        public static SingletonDemo getObject(){
            return instance;
        }
    }
  • 测试类

    package com.itheima.static06;
    ​
    /**
     * 单例模式测试类
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 17:51
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 单例模式测试类
     */
    public class SingletonDemo {
        public static void main(String[] args) {
            // 多次获取对象
            // Singleton singleton = new Singleton();
    ​
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            Singleton singleton3 = Singleton.getInstance();
    ​
    ​
            // 验证得到的对象是否只有一个
            System.out.println("singleton3 = " + singleton3);
            System.out.println("singleton2 = " + singleton2);
            System.out.println("singleton1 = " + singleton1);
    ​
    ​
        }
    }
  • 控制台输出内容

    package com.itheima.static06;
    ​
    /**
     * 单例模式测试类
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 17:51
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 单例模式测试类
     */
    public class SingletonDemo {
        public static void main(String[] args) {
            // 多次获取对象
            // Singleton singleton = new Singleton();
    ​
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            Singleton singleton3 = Singleton.getInstance();
    ​
    ​
            // 验证得到的对象是否只有一个
            System.out.println("singleton3 = " + singleton3);
            System.out.println("singleton2 = " + singleton2);
            System.out.println("singleton1 = " + singleton1);
    ​
    ​
        }
    }

1.5 代码块

1.5.1 概念

  • 代码块:{}包括的一块代码,就是代码块

  • 代码块分类:静态代码块、构造代码块

1.5.2 静态代码块

格式:

static{
    代码块中内容
}

特点:

  • 类加载时自动执行

  • 由于类只会加载一次,所以静态代码块也只会在类加载时执行一次

作用:

  • 完成类中静态成员的初始化

演示代码:

  • Student.java

    package com.itheima.static07;
    ​
    /**
     * 演示静态代码块
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 18:02
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 演示静态代码块
     */
    public class Student {
        static String school;
    ​
        // 定义静态代码块
        static {
            System.out.println("静态代码块执行了");
            school = "杭州黑马";
        }
    ​
    }

  • 测试类

    package com.itheima.static07;
    ​
    /**
     * 静态代码块测试类
     *
     * @Author Vsunks.v
     * @Date 2023/2/17 18:09
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 静态代码块测试类
     */
    public class StudentDemo {
    ​
        public static void main(String[] args) {
    ​
            // 类加载时自动执行静态代码块
            // 类只会加载一次,所以静态代码块也只会在类加载时执行一次
            System.out.println(Student.school);
            System.out.println(Student.school);
            System.out.println(Student.school);
            System.out.println(Student.school);
    ​
            System.out.println(new Student().school);
    ​
        }
    }
  • 控制台

    静态代码块执行了
    杭州黑马
    杭州黑马
    杭州黑马
    杭州黑马
    杭州黑马

1.5.3 构造代码块

格式:

{
    代码块中内容
}

特点:

  • 创建对象时,在构造器前自动执行

  • 一般类可以创建多个对象,所以构造代码块可以被执行多次。

作用:

  • 完成对象(中非静态成员)的初始化

  • 多个构造方法中相同的内容,可以定义在构造代码块中,简化重复编码。

演示代码:

  • Student.java

    package com.itheima.static08;
    ​
    /**
     * 构造代码块演示
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 构造代码块演示。需求:要求所有学生默认年龄为18
     */
    public class Student {
        String name;
        int age;
        static String school;
    ​
        // 提供构造方法,方法内部为年龄赋值为18
        /* public Student() {
            this.age = 18;
        }
    ​
        public Student(String name) {
            this.age = 18;
            this.name = name;
        } */
        
        // 定义静态代码块
        static {
            System.out.println("静态代码块执行了");
        }
    ​
        // 定义构造代码块,抽取构造方法中共性代码
        {
            System.out.println("构造代码块执行了");
            this.age = 18;
        }
    ​
        public Student() {
            System.out.println("无参构造执行了");
            // this.age = 18;
        }
    ​
        public Student(String name) {
            System.out.println("有参构造执行了");
            // this.age = 18;
            this.name = name;
        }
    }
    ​

  • 测试类

    package com.itheima.static08;
    ​
    /**
     * 构造代码块测试类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 构造代码块测试类
     */
    public class StudentDemo {
        public static void main(String[] args) {
            // 先使用静态变量。这个时候加载了Student类,但是并没有执行构造代码块
            Student.school = "杭州黑马";
    ​
            // 使用无参构造创建对象,调用了无参构造,执行了构造代码块
            Student stu1 = new Student();
            System.out.println(stu1.age);
    ​
            // 使用有参构造创建对象,调用了有参构造,执行了构造代码块
            Student stu2 = new Student("小黑");
            System.out.println(stu2.age);
    ​
            // 查看构造代码块的执行情况
    ​
    ​
        }
    }
    ​
  • 控制台

    静态代码块执行了
    构造代码块执行了
    无参构造执行了
    18
    构造代码块执行了
    有参构造执行了
    18

总结:

类中元素的执行先后顺序

静态代码块 》 构造代码块 》 构造方法

==2. 继承入门==

2.1 快速入门

2.1.1 概念

  • 继承:一种对象获取父对象的属性和行为的机制。类比生活中的子承父业。

  • 是面向对象三大特征之一

  • 父类:被继承的类,也成为基类、超类

    子类:继承的类,也成为派生类

  • 特点:

    1. 子类可以继承并使用父类中非私有的成员(成员变量、成员方法)

    2. 子类中也可以有自己特有的成员(成员变量、成员方法)

2.1.2 格式

继承通过extends实现

class 子类 extends 父类 { }

  • Eg:

    class Zi extends Fu{
        // 各种成员
    }

2.1.3 演示代码

  • 父类Fu.java

    package com.itheima.extends01;
    ​
    /**
     * 入门案例的Fu类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 入门案例的Fu类
     */
    public class Fu {
        // 在父类中定义公共的成员
        public void publicShow(){
            System.out.println("public show方法执行中.....");
        }
    ​
        // 在父类中定义私有的成员
        private void privteShow(){
            System.out.println("private show方法执行中.....");
        }
    }
  • 子类Zi.java

    package com.itheima.extends01;
    ​
    /**
     * 入门案例的Zi类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 入门案例的Zi类
     */
    public class Zi extends Fu{
    ​
        // 在子类中定义自己的成员
        public void method(){
            System.out.println("Zi 中 method方法执行中....");
        }
    }
    ​
  • 测试类

    package com.itheima.extends01;
    ​
    public class ExtendsDemo {
        public static void main(String[] args) {
            // 创建子类对象
            Zi zi = new Zi();
    ​
    ​
            // 调用子类自己的方法。
            // 子类可以有自己特有的成员
            zi.method();
    ​
    ​
            // 调用从父类继承的方法
            zi.publicShow();      // 可以继承并访问父类中非私有的成员
            // zi.privateShow();  // 不能继承并访问父类中私有的成员
    ​
        }
    }
    ​

2.2 优势和弊端

2.2.1 优势

使用了继承,会有如下优势(好处):

  • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)

  • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)

2.2.2 案例

需求:

分析&步骤:

  1. 定义Teacher类

  2. Teacher类中定义私有成员变量name、skill及其对应的getter&setter

  3. Teacher类中定义成员方法printInfo方法,方法体中打印名称和技能

  4. 定义Consultant类

  5. Consultant类中私有成员变量name、number及其对应的getter&setter

  6. Consultant类中定义成员变量printInfo,方法体中打印名称和服务过的咨询人数

  7. 分析发现两个类有相同的成员变量name及其对应的setter&getter,抽取到父类Person中定义,并让Teacher和Consultant类继承Person类

图示:

演示代码

  • Person.java

    package com.itheima.extends02;
    ​
    /**
     * 父类:人
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 父类:人
     */
    public class Person {
    ​
        /**
         * 姓名
         */
        private String name;
    ​
    ​
        public String getName() {
            return name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    }
    ​

  • Teacher.java

    package com.itheima.extends02;
    ​
    /**
     * 子类:讲师类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Teacher extends Person {
    ​
        /**
         * 技能
         */
        private String skill;
    ​
    ​
        public String getSkill() {
            return skill;
        }
    ​
        public void setSkill(String skill) {
            this.skill = skill;
        }
    ​
        public void printInfo() {
            System.out.println(getName() + "具备的技能是:" + skill);
        }
    }
  • Consultant.java

    package com.itheima.extends02;
    ​
    /**
     * 子类:咨询师类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 咨询师类
     */
    public class Consultant extends Person {
    ​
        /**
         * 咨询的人数
         */
        private int number;
    ​
    ​
        public int getNumber() {
            return number;
        }
    ​
        public void setNumber(int number) {
            this.number = number;
        }
    ​
        public void printInfo() {
            System.out.println(getName() + "服务的咨询人数:" + number);
        }
    }
    ​
  • ExtendsDemo.java测试类

    package com.itheima.extends02;
    ​
    /**
     * 继承的优势演示测试类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承的优势演示测试类
     */
    public class ExtendsDemo {
        public static void main(String[] args) {
            // 创建讲师对象
            Teacher tea = new Teacher();
    ​
            // 为讲师对象设置 获取 属性
            tea.setName("播仔");
            tea.setSkill("Java全栈");
    ​
            System.out.println(tea.getName());
            System.out.println(tea.getSkill());
    ​
            // 调用子类printInfo方法
            tea.printInfo();
    ​
        }
    }
  • 执行结果

    播仔
    Java全栈
    播仔具备的技能是:Java全栈

结论:

  • 继承可以提高代码的复用性

练习:

  • 基于上述代码,在测试类中创建咨询师类的对象,并调用方法测试;验证继承的好处:提高代码的复用性。

2.2.3 弊端

使用了继承,会有如下弊端:

  • 增强类之间的耦合性(Fu类和Zi类两个类之间产生了依赖的耦合关系)

  • 削弱了子类的独立性(当父类发生变化时,子类继承的内容也不得不跟着变化)

2.2.4 案例

需求:

  • 修改2.2.2中方法,使得获取名称时,自动添加前缀“淘气的”。

    eg:如果姓名为波仔,获取名称时得到的是“淘气的波仔

分析&步骤:

  1. 修改Person类的getName方法,在name前拼接指定的字符串

  2. 测试类中,分别创建不同的Teacher对象和Consultant对象,并获取名称

  3. 其他代码保持不变

  4. 运行测试类,查看效果

演示代码

  • Person.java

    package com.itheima.extends02;
    ​
    /**
     * 父类:人
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 父类:人
     */
    public class Person {
    ​
        /**
         * 姓名
         */
        private String name;
    ​
    ​
        public String getName() {
            return "淘气的" + name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    }

  • Teacher.java

    //保持不变
  • Consultant.java

    //保持不变
  • ExtendsDemo.java测试类

    package com.itheima.extends02;
    ​
    /**
     * 测试类。
     * <p>
     * 类中创建Teacher、consultant对象,为对象属性赋值并调用方法
     *
     * @Author Vsunks.v
     * @Date 2023/2/15 8:05
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 测试类。
     */
    public class ExtendsDemo {
    ​
        public static void main(String[] args) {
            // 目标:理解继承的好处。
            // 1. 创建老师对象
            Teacher t = new Teacher();
    ​
            // 2. 为成员变量赋值,包含子类和父类的成员变量
            t.setName("播仔");
            t.setSkill("Java、Spring");
            System.out.println(t.getName());
            System.out.println(t.getSkill());
            t.printInfo();
            
            // 3. 创建咨询师对象
            Consultant con = new Consultant();
            
            // 4. 为成员变量赋值
            con.setName("黑妞");
            con.setNumber("1000");
            System.out.println(con.getName());
            System.out.println(con.getNumber());
            con.printInfo();
            
        }
    }
  • 执行结果

    黑妞
    1000
    淘气的黑妞已经为1000人提供了咨询服务

2.3 注意事项

要求:理解并记忆。

  • Java不支持多继承,只支持单继承

  • Java支持多层继承

  • Object

    • Object是Java中所有的类的父类

    • 每个类都直接或间接的继承了Object类

    • IDEA中,可以通过Ctrl + H 查看每个类的继承关系

    • Java中所有的类都直接或间接继承自Object类

==3. 方法重写==

3.1 访问权限修饰符

3.1.1 概念

访问权限修饰符:用来限制类中的成员(成员变量、成员方法、构造器、代码块…)能够被访问的范围的修饰符。

此类修饰符共有四个,效果如下:

3.1.2 应用场景

  • 可以根据IDEA提示,通过是否能点出来 ,来验证是否有权限访问。

  • 常用的就是private和public

  • 默认可以理解为包范围,超出包范围无法访问。

3.1.3 演示代码

  • Fu.java父类中定义各种权限修饰符修饰的方法,并测试在本类中的访问效果

    package com.itheima.extends03;
    ​
    public class Fu {
        // 1、私有:只能在本类中访问
        private void privateMethod(){
            System.out.println("==private==");
        }
    ​
        // 2、缺省(默认):本类,同一个包下的子类无关类
        void defaultMethod(){
            System.out.println("==缺省==");
        }
    ​
        // 3、protected: 本类,同一个包下的子类无关类,不同包下的子类
        protected void protectedMethod(){
            System.out.println("==protected==");
        }
    ​
        // 4、public: 本类,同一个包下的子类无关类,不同包下的子类,不同包下的无关类
        public void publicMethod(){
            System.out.println("==public==");
        }
    ​
        public void test(){
            // 在本类中,所有权限都可以被访问到,且不需要使用类名/对象名调用
            privateMethod(); //正确
            defaultMethod(); //正确
            protectedMethod(); //正确
            publicMethod(); //正确
        }
    }
  • 同包下子类

    package com.itheima.extends03;
    ​
    public class Zi extends Fu {
        // 同包下的子类中,能访问到public、protected、默认修饰符修饰的方法
        public static void main(String[] args) {
            Fu fu = new Fu();
            // fu.privateMethod();  // 报错
            fu.defaultMethod();     // 正确
            fu.protectedMethod();   // 正确
            fu.publicMethod();      // 正确
    ​
        }
    ​
    ​
        // 因为有继承关系,继承下来的方法可以直接调用,而不用通过Fu类对象调用
        public void test() {
            // privateMethod();  // 报错
            defaultMethod();     // 正确
            protectedMethod();   //正确
            publicMethod();      //正确
        }
    }
  • 同包下无关类

    package com.itheima.extends03;
    ​
    public class Demo {
        // 同包下的无关中,能访问到public、protected、默认修饰符修饰的方法
        public static void main(String[] args) {
            Fu f = new Fu();
            // f.privateMethod();   //私有方法无法使用
            f.defaultMethod();      // 正确
            f.protectedMethod();    // 正确
            f.publicMethod();       // 正确
        }
    }
  • 不同包下子类

    package com.itheima.extends02;
    ​
    ​
    import com.itheima.extends03.Fu;
    ​
    public class Zi extends Fu {
        //在不同包下的子类中,只能访问到public、protected修饰的方法
        public void test(){
            // privateMethod(); // 报错
            // defaultMethod(); // 报错
            protectedMethod();  //正确
            publicMethod();     //正确
        }
    }
  • 不同包下无关类

    package com.itheima.extends02;
    ​
    ​
    import com.itheima.extends03.Fu;
    ​
    public class Demo2 {
        public static void main(String[] args) {
            // 不同包下的无关类,只能访问到public修饰的方法
            Fu f = new Fu();
            // f.privateMethod();   // 报错
            // f.defaultMethod();           //报错
            // f.protectedMethod();  //报错
            f.publicMethod();       //正确
        }
    }

3.2 方法重载(回顾)

回顾方法重载的细节。

  1. 同一个类中或者继承关系中

  2. 存在方法名相同

  3. 形参列表不同

的多个方法之间构成方法重载。

其中:

  1. 形参列表中参数个数、参数类型、参数顺序任意一个不同,则为形参列表不同。

  2. 方法重载与返回值无关

==3.3 方法重写==

3.3.1 概念

  1. 子父类继承关系中

  2. 存在方法签名完全相同的两个方法

之间就是方法重写

3.3.2 演示代码

  • Fu.java父类中定义方法

    package com.itheima.extends04;
    ​
    /**
     * 方法重写演示:父类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 方法重写演示:父类
     */
    public class Fu {
    ​
        public void study() {
            System.out.println("学习开店");
        }
    ​
        public void work() {
            System.out.println("开小店,赚小钱");
        }
    ​
        /**
         * 定义方法,演示重写的注意事项3:
         * 子类重写方法的返回值类型,范围小于等于父类总被重写方法的返回值类型
         * @return
         */
        public Number calc(){
            return 0;
        }
    }
  • Zi.java子类中重写方法

    package com.itheima.extends04;
    ​
    /**
     * 方法重写演示:子类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 方法重写演示:子类
     */
    public class Zi extends Fu {
    ​
        // 想要沿袭父类功能,但是又想有不同的实现方式时,可以使用方法重写。
    ​
        // 方法重写
    ​
        @Override   // 这是一个注解,作用就是校验被标注的方法,是否符合重写的要求格式;不符合则报错
        public void study() {
            System.out.println("公司经营、资金运作、团队管理");
        }
    ​
        @Override
        public void work() {
            System.out.println("开跨国公司,挣大钱");
        }
    ​
        @Override
        public Long calc(){
            return 1L;
        }
    }
    ​
  • Test.java测试类中创建子类对象,并调用方法

    package com.itheima.extends04;
    ​
    /**
     * 方法重写演示:测试类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 方法重写演示:测试类
     */
    public class Demo {
        public static void main(String[] args) {
            // 创建子类对象
            Zi zi = new Zi();
    ​
            // 调用子类方法
            zi.study();
            zi.work();
        }
    }
    ​
  • 控制台点结果

    /**
    ​
    ​
    */

3.3.3 注意事项

  1. @Override注解可以标注在子类重写方法上,用于校验重写格式是否正确

  2. 私有方法、静态方法不会被重写;

  3. 子类重写方法的访问权限修饰符不能低于父类 public >protected> 默认(不写) > private

  4. 子类重写方法的返回值类型,范围小于等于父类总被重写方法的返回值类型

3.3.4 工作中用法

原则:越简单越好。

  • 访问权限修饰符和父类中方法一致、返回值类型与父类一致。

总结:(方法签名)声明不变,(方法体)重新实现

3.3.5 应用场景

  • 但凡是父类中提供的功能,无法满足子类需求时,子类中就可以重写父类的方法

  • 子类重写Object的toString()方法,以便返回对象的内容。

场景演示案例(toString()):

  • 需求:

    打印学生对象时,不要显示其地址值(Hash值)

  • 理论基础

    直接打印对象时,打印的其实是调用其toString()方法的返回值

    Student类在没有手动继承任何类的时候,默认继承自Object类

    Student类中没有定义toString()方法,但是仍可以调用,是因为其父类Object中已经定义好了

    Object类中定义的toString()方法就是通过hashCode拼接的。

    子类Student中可以重写toString方法,显示成员内容而非地址值。

  • 演示代码

    Student.java学生类

    package com.itheima.extends05;
    ​
    /**
     * 方法重写应用场景:toString
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 方法重写应用场景:toString
     */
    public class Student /* extends Object */ {
        private String name;
        private int 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;
        }
    ​
        /**
         * 重复Object中的toString方法,方法内部使用成员变量的值拼接字符串并返回
         *
         * @return
         */
        /* @Override
        public String toString() {
    ​
            return "学生" + name + "的年龄为" + age;
        } */
    ​
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    ​

    Test.java 测试类

    package com.itheima.extends05;
    ​
    /**
     * 方法重写应用场景测试类
     *
     * @Author Vsunks.v
     * @Date 2023/2/18 14:44
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 方法重写应用场景测试类
     */
    public class StudentDemo {
        public static void main(String[] args) {
            // 创建学生对象
            Student stu = new Student();
    ​
            /*
                直接打印对象。默认打印的就是对象toString方法的返回值
    ​
                Student类中并没有定义该toString()
                那使用的是其父类Object中定义好的toString()
    ​
                Object中toString代码如下:
                     public String toString() {
                        return getClass().getName() + "@" + Integer.toHexString(hashCode());
                    }
                得出结论:
                    该方法返回的是 全类名@哈希值,之前为了方便理解,所以说哈希值为地址值。
                    生成哈希值的方式有多种,使用地址值生成只会其中的一种方式,而且还不是默认方式。
    ​
    ​
                我们打印对象时,一般想看到的是对象成员变量的值。
                再结合理论:直接打印对象。默认打印的就是对象toString方法的返回值
    ​
                我们可以为Student类重写toString方法,在方法内部使用成员变量的值拼接返回的字符串。
             */
    ​
            // com.itheima.extends05.Student@4554617c
            System.out.println("stu = " + stu);
    ​
            // 打印对象toString方法的返回值
            // com.itheima.extends05.Student@4554617c
            System.out.println("stu.toString() = " + stu.toString());
    ​
        }
    }

==4. 继承中成员访问特点==

主要探讨在子类中访问其他成员(成员变量、成员方法、构造方法)的问题。

==4.1 成员变量/方法==

4.1.1 概念

总原则:就近原则

按照以下顺序从上往下依次查找对应的成员并使用;

找到了就直接用,找不到就依次往下找;如果所有位置都不能找到应的成员,则报错。

  1. 局部位置(在有时)

  2. 子类成员位置

  3. 父类成员位置(不考虑多层继承)

4.1.2 代码演示-成员变量

  • 父类Fu.java

    package com.itheima.extends06;
    ​
    /**
     * 继承关系中成员变量访问特点:父类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员变量访问特点
     */
    public class Fu {
        /**
         * Fu类中成员变量name
         */
        String name = "Ikun";
    }
  • 子类Zi.java

    package com.itheima.extends06;
    ​
    /**
     * 继承关系中成员变量访问特点:子类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员变量访问特点:子类
     */
    public class Zi extends Fu {
        /**
         * Zi类中成员变量name
         */
        private String name = "杰哥";
    ​
        public void showName(){
            // 局部位置变量 name
            // 问题:局部变量隐藏同名成员变量的问题,这个就是就近原则的一个特列
            String name = "丁震";
            System.out.println(name);
        }
    }
  • 测试类Demo.java

    package com.itheima.extends06;
    ​
    /**
     * 继承关系中成员变量访问特点:测试类
     *
     * @Author Vsunks.v
     * @Date 2023/2/18 15:20
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员变量访问特点:测试类
     */
    public class Demo {
        public static void main(String[] args) {
    ​
            // 创建子类对象
            Zi zi = new Zi();
    ​
            // 运行代码查看子类中使用name时,用的是哪个name
            zi.showName();
        }
    }

4.1.3 代码演示-成员方法

  • 父类Fu.java

    package com.itheima.extends07;
    ​
    /**
     * 继承关系中成员方法访问特点:父类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员方法访问特点:父类
     */
    public class Fu {
    ​
        /**
         * 父类中成员方法show
         */
        public void show() {
            System.out.println("Fu中的show()方法被调用");
        }
    ​
    }
  • 子类Zi.java

    package com.itheima.extends07;
    ​
    /**
     * 继承关系中成员方法访问特点:子类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员方法访问特点:子类
     */
    public class Zi extends Fu {
    ​
        /**
         * Zi类中成员方法show
         */
        public void show() {
            System.out.println("Zi中的show()方法被调用");
        }
    ​
        // 定义方法,调用show方法。查找调用遵循就近原则
        public void invokeShow(){
            show();
        }
    ​
    }
    ​
  • 测试类Demo.java

    package com.itheima.extends07;
    ​
    /**
     * 继承关系中成员方法访问特点:测试类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员方法访问特点:测试类
     */
    public class Demo {
    ​
        public static void main(String[] args) {
    ​
            // 创建子类对象
            Zi zi = new Zi();
    ​
            // 调用子类对象的成员方法,查看打印结果
            // (依次注释掉子类成员位置、父类成员位置定义的show方法,查看代码执行/编译效果)
            zi.invokeShow();
    ​
            // 调用zi fu类中都没有的成员方法,演示查找失败的情况
            // zi.showxxx();
    ​
        }
    }
    ​

4.1.4 访问特定位置成员

在继承关系中,如果子父类提供了相同的成员方法/成员变量,按照就近原则,子类中默认访问优先级最高的;那能不能手动指定访问某个位置的成员呢?

即:需求:

  1. Zi/Fu类中存在同名的成员变量和成员方法时

  2. 想要访问特定位置的成员

这时,可以使用this和super

概念:

  • this:代表本类对象的引用。成员方法中均有this,代表的是调用该方法的对象。

  • super:代表父类存储空间的标识(可以理解为父类对象引用)

使用:

  • this.成员:访问本类中的成员(成员变量、成员方法)

  • super.成员:访问父类中的成员(成员变量、成员方法)

演示代码:

  • 成员变量:基于4.1.2中代码,修改Zi类,其他代码不变

    package com.itheima.extends06;
    ​
    /**
     * 继承关系中成员变量访问特点:子类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员变量访问特点:子类
     */
    public class Zi extends Fu {
        /**
         * Zi类中成员变量name
         */
        private String name = "杰哥";
    ​
        public void showName(){
            // 局部位置变量 name
            // 问题:局部变量隐藏同名成员变量的问题,这个就是就近原则的一个特列
            String name = "丁震";
            System.out.println(name);
            System.out.println("子类成员位置:this.name = " + this.name);
            System.out.println("父类成员位置:super.name = " + super.name);
        }
    ​
    }
  • 成员方法:基于4.1.3中代码,修改Zi类,其他代码不变

    package com.itheima.extends07;
    ​
    /**
     * 继承关系中成员方法访问特点:子类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中成员方法访问特点:子类
     */
    public class Zi extends Fu {
    ​
        /**
         * Zi类中成员方法show
         */
        public void show() {
            System.out.println("Zi中的show()方法被调用");
        }
    ​
        // 定义方法,调用show方法
        public void invokeShow(){
            show();
            // 调用子类中的成员方法,因为是在同一个类中,所以调用者this可以省略不写
            this.show();
            // 调用父类成员方法
            super.show();
        }
    ​
    }

  • 成员变量演示代码中

  • 成员方法演示代码中,在测试类中访问Fu类的show方法

4.2 构造方法

4.2.1概念

子类所有构造方法默认都会先访问父类中无参构造,再执行自己构造方法中的逻辑。(高斯林说的)

子类构造方法的第一条语句默认都是super();

4.2.2 代码演示

  • 父类Fu.java

    package com.itheima.extends08;
    ​
    /**
     * 继承关系中构造方法访问特点:父类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中构造方法访问特点:父类
     */
    public class Fu {
        // 手动编写一个无参构造,构造方法内部打印一句话。
        public Fu(){
            System.out.println("父类中无参构造执行了……");
        }
    }
    ​

    ####

  • 子类Zi.java

    package com.itheima.extends08;
    ​
    /**
     * 继承关系中构造方法访问特点:子类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中构造方法访问特点:子类
     */
    public class Zi extends Fu{
    ​
        // 定义子类无参构造
        public Zi(){
            System.out.println("子类无参构造执行了……");
        }
    ​
        // 定义子类有参构造
        public Zi(String name){
            super();
            System.out.println("子类有参构造执行了……");
        }
    }

  • 测试类Demo.java

    package com.itheima.extends08;
    ​
    /**
     * 继承关系中构造方法访问特点:测试类
     *
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description: 继承关系中构造方法访问特点:测试类
     */
    public class Demo {
        public static void main(String[] args) {
            // 使用多种方式创建子类对象,查看子父类构造方法的执行顺序
    ​
            // 使用子类无参构造创建对象
            Zi zi1 = new Zi();
    ​
            // 使用子类有参构造创建对象
            Zi zi2 = new Zi("章子以");
        }
    }
    ​

  • 断点调试,查看执行顺序

    父类中无参构造执行了……
    子类无参构造执行了……
    父类中无参构造执行了……
    子类有参构造执行了……

总结:

在本类的构造方法中,可以使用super调用父类的构造方法

  • super(): 调用父类的空参数构造方法

  • super(实参): 调用父类对应参数的有参构造方法

稍后代码演示

4.2.3 原理分析

  1. 为什么要默认调用父类的构造方法

    答:

    1. 子类会继承父类非私有的成员变量,继承后就可以使用。

    2. 如果创建子类对象时,只执行子类自己的构造;创建好子类对象之后,子类对象有可能使用到了父类中未初始化的成员变量,则会造成成员变量未初始化的问题;

    3. 所以在子类构造方法执行前,要先执行父类的构造方法,初始化父类成员变量;然后再执行子类构造方法中逻辑

  2. 为什么被调用的父类构造一定默认是无参构造,父类其他的有参构造可以吗?(使用简图看图说话)

    1. 所有类都直接或间接继承自Object类,每级子类的构造方法都要调用父类的构造用以初始化父类成员;

    2. 但是Object类中只有无参构造,所以Object的子类只能调用Object类的无参构造;

    3. 又因为所有类默认只有无参构造,所以从通用性考虑,Object子类的子类最好调用Object子类的无参构造。

  3. 默认调用父类的无参构造的注意事项

    1. 每一个子类构造方法的第一条语句默认都是:super();

      • 如果没有显式写出,默认隐式的有一行super();,用于调用父类无参构造;

      • 也可以显式的书写super(),或者添加参数调用父类指定的带参构造:super(实参列表) 显式书写之后,系统将不再隐式添加super()相关内容

      • 该语句必须位于子类构造方法的第一行

4.2.4 应用场景

手动调用父类构造方法的应用场景,或者说是:父类没有无参构造怎么办?

问:如果父类中没有提供无参构造怎么办?

答:手动在子类构造方法第一行,使用super(实参)调用父类存在的有参构造。

演示代码:

  • 父类Fu.java

    package com.itheima.extends08;
    ​
    /**
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Fu {
        public Fu(String name){
            System.out.println("Fu类中有参构造执行了,参数name值为"+name);
        }
    }

    ####

  • 子类Zi.java

    package com.itheima.extends08;
    ​
    /**
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Zi extends Fu{
    ​
        public Zi(){
            super("xxx");
            System.out.println("Zi类中无参构造方法执行了……");
        }
    ​
        public Zi(String name){
            super(name);
            System.out.println("Zi类中有参构造执行了……");
        }
    }
    ​

  • 测试类Demo.java

    package com.itheima.extends08;
    ​
    /**
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Demo {
    ​
        public static void main(String[] args) {
            // 使用无参构造创建子类对象
            Zi zi1 = new Zi();
    ​
            // 使用有参构造创建子类对象
            Zi zi2 = new Zi("无名氏");
    ​
        }
    }

  • 断点调试,查看执行顺序

    父类中有参构造执行了……xxx
    子类无参构造执行了……
    父类中有参构造执行了……无名氏
    子类有参构造执行了……

4.2.5 访问本类中其他构造

在当前类的构造方法中,可以使用this调用本类中其他构造方法

  • this(): 调用本类的空参数构造方法

  • this(实参): 调用本类对应参数的有参构造方法

演示代码:

  • 父类Fu.java

    package com.itheima.extends09;
    ​
    /**
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Fu {
        public Fu(){
            System.out.println("Fu类中无参构造执行了……");
        }
    }
  • 子类Zi.java

    package com.itheima.extends09;
    ​
    /**
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Zi extends Fu{
    ​
        public Zi(){
            System.out.println("Zi类中无参构造方法执行了……");
        }
    ​
        public Zi(String name){
            System.out.println("Zi类中一个参数的有参构造执行了……");
        }
        
        public Zi(String name,int age){
            this(name);
            System.out.println("使用一下age……"+age);
            System.out.println("Zi类中两个参数的有参构造执行了……");
        }
    }
    ​
  • 测试类Demo.java

    package com.itheima.extends09;
    ​
    /**
     * @Author Vsunks.v
     * @Blog blog.sunxiaowei.net/996.mba
     * @Description:
     */
    public class Demo {
    ​
        public static void main(String[] args) {
            // 使用无参构造创建子类对象。执行流程和之前一致,跳过。
            // Zi zi1 = new Zi();
    ​
            // 使用有参构造创建子类对象
            Zi zi2 = new Zi("无名氏");
    ​
        }
    }

注意事项:

  1. this()需要放在代码的第一行

  2. this() 和 super() 不能同时出现在同一个构造方法中

  3. this()不能(直接或间接)调用本身(当前构造方法),会无限递归

  4. 但凡是构造方法第一行显示的书写了this(..)或者super(..),系统将不再提供默认的super();

==4.2.6 this和super使用总结==

this:代表本类对象的引用。成员方法中均有this,代表的是调用该方法的对象。

super:代表父类存储空间的标识(可以理解为父类对象引用)

访问本类成员: this.成员变量 //访问本类成员变量 this.成员方法 //调用本类成员方法 this() //调用本类无参数构造器 this(实参) //调用本类有参数构造器 访问父类成员: super.成员变量 //访问父类成员变量 super.成员方法 //调用父类成员方法 super() //调用父类无参数构造器 super(实参) //调用父类有参数构造器

注意:this和super访问构造方法,只能放在构造方法一行且不可共存,否则会报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值