今日内容
-
Static
-
==继承==
目录
0. 面向对象导学
0.1 知识特点
-
概念多
-
重理解
-
代码少
0.2 学习方式
-
面向对象之前,要多练,找手感,巩固知识
-
面向对象相关知识,要多理解
-
理解的基础上练习
Java是源于生活的,对比生活理解Java中面向对象思想
-
重点在理解,代码量很少、
慢慢理解,不要有压力;慢慢练习体验,等待顿悟的那一刻
-
0.3 重点
-
以对象为核心,设计并使用对象保存、处理数据,并最终解决各种问题。
1. static
1.0 面向对象知识回顾
-
概念
类:对生活中具有相同属性和行为的一类事物的描述/抽象
对象:某一类别下某个具体的个体
两者关系:类是对象的描述,对象是类的实体
原则:Java中万物皆对象
成员变量:属性,参数。
-
独属于某个对象,不与同类下其他对象共享。
-
只能通过对象名.成员变量名调用
成员变量代码演示
-
定义成员变量
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对象的时候必然要调用构造方法,所以要在构造方法中完成数量的累加
思路:
-
定义User类
-
User类中定义静态变量number(初始值为0)
-
User类的构造方法中number+1
-
定义测试类UserDemo,并编写main方法,
-
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 演示代码
-
步骤
-
定义一个Student类
-
在类中定义一个成员变量,分数score
-
在类中定义一个静态方法printHelloWorld,内部打印一句话
-
定义一个非静态成员方法printPass,根据分数判断是否及格
-
定义一个测试类StaticMethodDemo,其中定义main方法
-
main方法中分别调用静态方法
6.1 使用Student类直接调用静态方法
6.2 使用Student类的对象调用静态方法
-
调用非静态成员方法,注意两者调用方式的区别。
-
-
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
关键字;成员方法中可以使用thisthis
:当前对象的引用(之后会讲)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() { } }
思考:
-
main方法是什么方法
// static修饰的静态方法 public static void main(String[] args){}
-
在学习方法时,为什么自定义的方法都要被static修饰
/** 定义后方法之后,我们需要调用测试,以便看到效果。 基于当时已学内容,只能在main方法中调用测试 基于静态只能调用静态的原则 被main方法调用的其他自定义方法,只能被static修饰 */
1.3.7 应用场景
-
静态方法可以直接使用类调用,不再依赖于对象,有悖于面向对象思想,所以使用场景较少
-
常见场景:工具类/单例模式(需要配合静态变量)。
==1.4 静态使用场景==
==1.4.1 场景1:工具类==
工具类概念:
-
一个类,内含可以轻松实现各种功能的方法
-
类中方法调用要简单方便,才可以称之为工具
总结:工具类中的方法多为静态方法,可以通过类名直接调用,方便使用。
验证码案例需求:
-
定义一个工具类,类中需要提供一个方法,该方法可以生成一个指定长度的验证码
-
验证码中字符可以包含:数字、小写字母、大写字母
分析:
-
工具类中方法多为静态,需要定义一个类,类中定义静态方法
-
验证码中字符是随机的,可以考虑配合随机数生成器使用
-
随机数得到的是一个随机的数字,和数字相关的概念有索引、个数、长度等
-
可以通过字符串的方法charAt()方法,获取指定索引处的字符
-
因为长度不确定,需要向生成验证码的方法传参以指定生成随机数的个数,也就是随机获取字符的个数
-
随机数生成器 + 字符串 + 索引,完成需求
思路:
-
定义工具类VeryfyCodeUtil
-
VeryfyCode类中定义静态方法generateCode
方法返回值为验证码,String类型
方法参数为验证码的长度n,int类型
-
方法内部 定义一个变量code,用以记录生成的验证码
-
方法内部 定义一个由所有可选字符组成的字符串data[0-9a-zA-Z]
-
方法内部 创建Random对象,用于生成随机数
-
方法内部 编写for循环,循环次数为n
-
循环体中,使用随机数生成器生成一个[0,data.length),并将其作为索引从data中获取该索引处的字符,最后将字符拼接进验证码变量code中
-
方法中循环外,返回生成好的验证码code
-
定义测试类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. 步骤:
-
定义类,并私有化构造方法
-
类中定义一个私有的自己类型的静态变量,并初始化
-
提供一个公共的静态方法,方法中返回静态变量
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 概念
-
继承:一种对象获取父对象的属性和行为的机制。类比生活中的子承父业。
-
是面向对象三大特征之一
-
父类:被继承的类,也成为基类、超类
子类:继承的类,也成为派生类
-
特点:
-
子类可以继承并使用父类中非私有的成员(成员变量、成员方法)
-
子类中也可以有自己特有的成员(成员变量、成员方法)
-
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 案例
需求:
分析&步骤:
-
定义Teacher类
-
Teacher类中定义私有成员变量name、skill及其对应的getter&setter
-
Teacher类中定义成员方法printInfo方法,方法体中打印名称和技能
-
定义Consultant类
-
Consultant类中私有成员变量name、number及其对应的getter&setter
-
Consultant类中定义成员变量printInfo,方法体中打印名称和服务过的咨询人数
-
分析发现两个类有相同的成员变量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:如果姓名为波仔,获取名称时得到的是“淘气的波仔”
分析&步骤:
-
修改Person类的getName方法,在name前拼接指定的字符串
-
测试类中,分别创建不同的Teacher对象和Consultant对象,并获取名称
-
其他代码保持不变
-
运行测试类,查看效果
演示代码
-
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 方法重载(回顾)
回顾方法重载的细节。
在
-
同一个类中或者继承关系中
-
存在方法名相同
-
形参列表不同
的多个方法之间构成方法重载。
其中:
-
形参列表中参数个数、参数类型、参数顺序任意一个不同,则为形参列表不同。
-
方法重载与返回值无关
==3.3 方法重写==
3.3.1 概念
在
-
子父类继承关系中
-
存在方法签名完全相同的两个方法
之间就是方法重写
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 注意事项
-
@Override注解可以标注在子类重写方法上,用于校验重写格式是否正确
-
私有方法、静态方法不会被重写;
-
子类重写方法的访问权限修饰符不能低于父类 public >protected> 默认(不写) > private
-
子类重写方法的返回值类型,范围小于等于父类总被重写方法的返回值类型
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 概念
总原则:就近原则
按照以下顺序从上往下依次查找对应的成员并使用;
找到了就直接用,找不到就依次往下找;如果所有位置都不能找到应的成员,则报错。
局部位置(在有时)
子类成员位置
父类成员位置(不考虑多层继承)
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 访问特定位置成员
在继承关系中,如果子父类提供了相同的成员方法/成员变量,按照就近原则,子类中默认访问优先级最高的;那能不能手动指定访问某个位置的成员呢?
即:需求:
在
-
Zi/Fu类中存在同名的成员变量和成员方法时
-
想要访问特定位置的成员
这时,可以使用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 原理分析
为什么要默认调用父类的构造方法
答:
子类会继承父类非私有的成员变量,继承后就可以使用。
如果创建子类对象时,只执行子类自己的构造;创建好子类对象之后,子类对象有可能使用到了父类中未初始化的成员变量,则会造成成员变量未初始化的问题;
所以在子类构造方法执行前,要先执行父类的构造方法,初始化父类成员变量;然后再执行子类构造方法中逻辑
为什么被调用的父类构造一定默认是无参构造,父类其他的有参构造可以吗?(使用简图看图说话)
所有类都直接或间接继承自Object类,每级子类的构造方法都要调用父类的构造用以初始化父类成员;
但是Object类中只有无参构造,所以Object的子类只能调用Object类的无参构造;
又因为所有类默认只有无参构造,所以从通用性考虑,Object子类的子类最好调用Object子类的无参构造。
默认调用父类的无参构造的注意事项
每一个子类构造方法的第一条语句默认都是: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("无名氏"); } }
注意事项:
this()需要放在代码的第一行
this() 和 super() 不能同时出现在同一个构造方法中
this()不能(直接或间接)调用本身(当前构造方法),会无限递归
但凡是构造方法第一行显示的书写了this(..)或者super(..),系统将不再提供默认的super();
==4.2.6 this和super使用总结==
this:代表本类对象的引用。成员方法中均有this,代表的是调用该方法的对象。
super:代表父类存储空间的标识(可以理解为父类对象引用)
访问本类成员: this.成员变量 //访问本类成员变量 this.成员方法 //调用本类成员方法 this() //调用本类无参数构造器 this(实参) //调用本类有参数构造器 访问父类成员: super.成员变量 //访问父类成员变量 super.成员方法 //调用父类成员方法 super() //调用父类无参数构造器 super(实参) //调用父类有参数构造器
注意:this和super访问构造方法,只能放在构造方法一行且不可共存,否则会报错。