2.继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
◆extends的意思是“扩展”。子类是父类的扩展。
◆JAVA中类只有单继承,没有多继承!
父类Person
package com.oop.demo02; //是学生、老师类的父类 //子类可以继承父类方法和属性(public) /* * public * protected * default 什么都没写 就是default这个 * private * */ public class Person {public int legacy=10_0000_0000;//这里定义的是public的变量private int estate=20_0000_0000;//这里定义的是私有属性,不给子类使用//注意这里的say方法是public修饰的public void say(){System.out.println("我说了一句话!");} }
子类学生类 Student
package com.oop.demo02; //学生继承了人 这个类 是person的子类/派生类 public class Student extends Person{//Person person; //组合,暂时还不知道什么意思 }
子类教师类
package com.oop.demo02; //老师类继承了 人这个类 是person的子类 public class Teacher extends Person {}
启动类Application
package com.oop.demo02; //启动类(包内包外都可以 import(包外)) //Demo02 面向对象 继承 //object类 //这是包外启动项 主要想测试下 import //还有public 等关键字 //Crtl+H 打开结构树 //在java的类都默认直接或者间接继承Object类! //java中只有单继承,没有多继承 一个儿子只能有一个爸爸,但是一个爸爸可以有多个儿子 public class Application {public static void main(String[] args) {Student s1 = new Student();s1.say();//这个方法是Student继承了人这个类的System.out.println(s1.legacy);//继承了父类的属性//System.out.println(s1.estate);//私有的不给子类用Teacher t1 = new Teacher();t1.say();//同理t1继承了人这个类的say方法System.out.println(s1.legacy);//继承了父类的属性} }
◆继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
◆继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示
◆子类和父类之间,从意义上讲应该具有"is a"的关系.
◆object类
◆super
父类Person
package com.oop.demo03;public class Person {protected String name="巢安龙";private int age=20;protected void print(){System.out.println("Person");} }
子类学生类Student
package com.oop.demo03; //继承的的包内Demo03 的 Person类 public class Student extends Person { public String name="胖呼呼";public void test(String name){System.out.println(name); //打印传进来的形参的 nameSystem.out.println(this.name);//打印实例化对象的 nameSystem.out.println(super.name);//打印父类的 name}public void print(){System.out.println("Student");}public void print_new(String content){System.out.println(content);print();this.print();super.print();//调用父类的方法}}
子类教师类 Teacher
package com.oop.demo03; //继承的的包内Demo03 的 Person类 public class Teacher extends Person{ public String name="longGe"; public void test(String name){System.out.println(name); //打印传进来的形参的 nameSystem.out.println(this.name);//打印实例化对象的 nameSystem.out.println(super.name);//打印父类的 name}}
应用类Application
package com.oop.demo03;//这里主要突出 继承super 的 //super 指代实例化对象的父类 可以调用父类的属性和方法(当然私有的private 无法被继承) //this 指代当前实例化对象 public class Apllication { public static void main(String[] args) {Student student = new Student();//super 调用父类的属性(private的不行)student.test("癞痢皮");Teacher teacher = new Teacher();teacher.test("chaoGe");//这两个方法名竟然可以一样//student.super//为什么在测试类里面用不了super 还是我的使用方法出了问题呢?//super 调用父类里面的方法! 看Student类里面的详细实现student.print_new("student_content");}}
输出结果:
癞痢皮 胖呼呼 巢安龙 chaoGe longGe 巢安龙 student_content Student Student Person
为什么在测试类里面用不了super 还是我的使用方法出了问题呢?
package com.oop.Demo03;//这里主要突出 继承super 的 //super 指代实例化对象的父类 可以调用父类的属性和方法(当然私有的private 无法被继承) //this 指代当前实例化对象 public class Apllication {public static void main(String[] args) {Student student = new Student();//student.test("癞痢皮");//Teacher teacher = new Teacher();//teacher.test("chaoGe");//这两个方法名竟然可以一样student.super//为什么在测试类里面用不了super 还是我的使用方法出了问题呢?} }
在你的代码中,
super
是一个关键字,用于引用父类(超类)中的构造方法、属性和方法。在 Java 中,super
不能在实例化对象的类方法中直接使用,而是用于访问当前对象的父类成员。为什么你无法在
Apllication
类中使用super
?
super
只能在 子类的实例方法中使用,目的是访问父类的成员(方法、变量、构造器等)。你不能在父类(如Apllication
类)的main
方法中直接使用super
,因为main
方法是静态方法,它并不属于任何具体的实例对象。- 如果你希望使用
super
,你需要在 子类中编写方法,使用super
来访问父类的成员。使用
super
的正确方法:
- 父类和子类的关系:子类通过继承父类,可以访问父类的公开成员(
public
和protected
)以及父类提供的方法和构造器。private
成员是不能被继承的,因此super
无法访问它们。super
用法示例
- 在子类中,使用
super()
调用父类构造器。- 使用
super.属性
或super.方法
来调用父类的成员或方法。代码示例
假设你有一个父类
Person
,和一个子类Student
,你可以在子类中使用super
来访问父类的成员。父类
Person
:package com.oop.Demo03;public class Person {protected String name; // protected 使得子类可以访问protected int age;public Person(String name, int age) {this.name = name;this.age = age;}public void introduce() {System.out.println("我叫 " + name + ",年龄 " + age);} }
子类
Student
:package com.oop.Demo03;public class Student extends Person {private String school;public Student(String name, int age, String school) {// 调用父类的构造方法super(name, age);//必须放在这个方法提第一行this.school = school;}public void introduce() {// 调用父类的 introduce 方法super.introduce(); // 访问父类的成员方法System.out.println("我来自 " + school);} }
测试类
Application
:package com.oop.Demo03;public class Application {public static void main(String[] args) {Student student = new Student("张三", 20, "清华大学");student.introduce();} }
解释:
super(name, age)
:在Student
的构造方法中,super
用来调用父类Person
的构造方法,以初始化父类的name
和age
属性。super.introduce()
:在Student
的introduce
方法中,通过super.introduce()
调用父类Person
的introduce
方法。super
在子类中使用:super
用于子类中访问父类的成员或构造方法,但不能在父类(如Application
类)中使用。你的错误:
你在
Application
类的main
方法中尝试使用super
,这是不允许的。因为super
是用来指代子类对象的父类成员的,所以它只能在子类的实例方法中使用。1. 如果没有定义构造器,Java 会默认提供一个无参构造器
- 正确,如果你没有定义任何构造器,Java 会自动提供一个无参构造器,且该构造器什么都不做。
public class Person {private String name;// Java 会自动提供如下无参构造器// public Person() { } }
你可以通过以下方式创建对象:
Person person = new Person(); // 调用自动生成的无参构造器
1.1 如果没有显式写无参构造器,也能直接创建对象
- 正确,如果你没有定义任何构造器,Java 会自动提供一个无参构造器。
- 你可以直接用
new
创建对象,而不需要显式定义无参构造器。public class Person {private String name;// 没有显式定义构造器,Java 会提供一个默认的无参构造器 }Person person = new Person(); // 正常创建对象
1.2 如果你定义了有参构造器,就不会自动提供无参构造器
- 正确,当你定义了有参构造器后,Java 不会再自动提供无参构造器。如果此时你希望使用无参构造器,你必须显式定义它,否则会报错。
public class Person {private String name;// 显式定义了有参构造器public Person(String name) {this.name = name;} }// 这会报错:因为没有无参构造器 Person person = new Person(); // 编译错误
如果你希望仍然能使用无参构造器来创建对象,就必须显式定义一个无参构造器:
public class Person {private String name;// 无参构造器public Person() {this.name = "默认名字";}// 有参构造器public Person(String name) {this.name = name;} }Person person1 = new Person(); // 使用无参构造器 Person person2 = new Person("张三"); // 使用有参构造器
1.3 子类创建对象时会默认先调用父类的构造器,默认调用父类的无参构造器
- 正确,当你创建一个子类对象时,Java 会默认调用父类的构造器。如果父类没有无参构造器,且你没有显式调用父类的有参构造器,就会报错。
- 如果父类只有有参构造器,而没有无参构造器,子类的构造器必须显式调用父类的有参构造器。
public class Person {private String name;// 只有有参构造器public Person(String name) {this.name = name;} }public class Student extends Person {// 子类无参构造器public Student() {// 必须显式调用父类的有参构造器super("张三"); // 调用父类的有参构造器} }// 这样可以创建 Student 对象 Student student = new Student(); // 正常创建对象,调用了父类的有参构造器
如果你没有在子类构造器里显式调用父类的构造器,且父类没有无参构造器,就会编译错误。
总结:
- 如果你没有定义构造器:Java 会自动提供一个无参构造器。
- 如果你定义了有参构造器:Java 不会自动提供无参构造器。如果需要无参构造器,你必须显式定义。
- 如果子类创建对象时,父类没有无参构造器:Java 会默认调用父类的无参构造器。如果父类没有无参构造器,子类必须显式调用父类的有参构造器(
super(参数)
)。- 子类创建对象时,必须先调用父类构造器,否则会报错,特别是在父类没有无参构造器时,必须显式调用父类的有参构造器。
是不是在自己写类的时候最好显式加无参构造器
在自己编写类时,是否显式添加无参构造器通常取决于几个因素,特别是你希望如何使用这个类,以及它的继承关系。总体而言,在大多数情况下,显式地添加无参构造器是一个好习惯,尤其是在以下几种场景中:
1. 需要使用无参构造器时
如果你希望通过无参构造器创建对象,或者某些库和框架(如 Java 序列化、反射、Spring 等)需要无参构造器来实例化类,显式定义一个无参构造器就非常重要。例如:
- 反射:有些框架或工具会通过反射来创建对象,这时需要有一个无参构造器。
- 序列化和反序列化:Java 的序列化机制通常会要求类有一个无参构造器,因为反序列化时需要先创建对象然后再设置字段的值。
2. 继承关系中的构造器调用
如果你的类作为父类被继承,子类默认会调用父类的构造器。如果父类没有显式定义无参构造器,且子类没有显式调用父类的有参构造器,子类的构造器会报错。因此,在父类中定义无参构造器可以避免一些潜在的错误。
3. 给类添加默认行为或值
无参构造器通常用于设置类的默认状态,尤其是在没有外部输入的情况下。例如:
public class Person {private String name;private int age;// 无参构造器,设置默认值public Person() {this.name = "未知";this.age = 0;}// 有参构造器,允许外部设置值public Person(String name, int age) {this.name = name;this.age = age;} }
在这个例子中,使用无参构造器会创建一个默认值的
Person
对象,而有参构造器可以根据需要设置特定的值。4. 兼容性
如果你将这个类交给其他开发人员使用或集成到框架中,添加无参构造器会提供更高的兼容性。这样,无论是框架,还是其他开发人员都能更方便地创建对象。
5. 反序列化和 JSON 处理
在处理 JSON 时,比如使用
Jackson
或Gson
等库,它们会使用无参构造器来创建对象,然后通过 setter 方法或字段直接填充数据。如果没有无参构造器,反序列化可能会失败。6. 避免潜在问题
如果类没有无参构造器且又没有显式地在其他构造器中调用父类的构造器,子类可能会无法正确实例化。因此,明确地定义一个无参构造器可以避免这种潜在的错误。
7. 特殊情况:当你有复杂初始化逻辑时
如果你的类有一些复杂的初始化逻辑,而无参构造器的行为难以保证,可能需要避免使用无参构造器。例如,当类需要接收特定的依赖项(如数据库连接、外部服务等)时,最好使用有参构造器来确保类在创建时得到正确的初始化。
小结:是否显式添加无参构造器
- 推荐做法:在多数情况下,尤其是类有多个构造器时,显式添加无参构造器是一个好习惯。这样做不仅能提供更好的兼容性,还能避免继承时的构造器问题。
- 例外情况:如果类的构造过程非常复杂,或者你不希望允许无参实例化对象(比如不想创建空对象),则可以选择不定义无参构造器。
总之,虽然 Java 编译器会在某些情况下自动提供无参构造器,但为了明确控制对象创建过程,并提高代码的可维护性和兼容性,显式添加无参构造器是一个值得推荐的做法。
super注息点:
1.super调用父类的构造方法,必须在构造方法的第一个2.super 必须只能出现在子类的方法或者构造方法中!
3、super和 this 不能同时用构造方法!
Vs this:代表的对象不同:this: 本身调用者这个对象super:代表父类对象的应用
前提
this:没有继承也可以使用super:只能在继承条件才可以使用构造方法
this();本类的构造super():父类的构造!
继承中构造器执行顺序
父类 Person
package com.oop.demo04;public class Person {private String name;//无参构造器 构造方法public Person() {System.out.println("Person无参构造执行了!");}//Person的有参构造器public Person(String name) {this.name = name;} }
子类学生类 Student
package com.oop.demo04;//继承的的包内Demo04 的 Person类 public class Student extends Person {private String name;//无参构造器 无参构造方法public Student() {//隐藏代码,可以不写的//super();//调用父类的构造器必须要在子类的第一行 所以如果父类定义了有参却没有定义无参 子类初始化就会报错//this("巢安龙");//调用本类的构造器(有参/无参) 本类的第一行// 由于继承了父类 所以子类创建对象时调用了构造器,就会会默认调用父类的无参,如果无参没有初始化就会报错 //解决办法 手动添加无参构造 或者直接调用父类的有参构造(有的话)super();//这是直接调用父类的无参构造器 继承的话 这句是可以省略的 但是如果写了就得放在此方法体的第一行//super("perple's name");//调用父类的构造器也必须要在子类的第一行System.out.println("Student无参构造执行了!");}public Student(String name) {//这里父类没有无参构造器 得加一句 super(参数)父类的有参构造器(有的话)super("name");this.name=name;} }
子类教师类 Teacher
package com.oop.demo04;//继承的的包内Demo04 的 Person类 public class Teacher extends Person {public Teacher() {super();}public Teacher(String name) {super("name");} }
启动类Application
package com.oop.demo04;//这里主要突出 继承super 的 //此软件包主要突出 super 对构造器的影响 //alt +insert 快捷插入构造器 public class Apllication {public static void main(String[] args) {Student student = new Student();//这里实例化Student类 调用其无参构造器,但是由于Student类继承了Person类,所以调用Student无参构造的时候也调用了Person的无参构造器} }
输出结果:
Person无参构造执行了! Student无参构造执行了!
注意点:
关键点:
super()
关键字:
- 用于在子类构造器中调用父类构造器。
super()
调用父类的无参构造器,super(参数)
调用父类的有参构造器。- 构造器调用顺序:
- 子类构造器首先调用父类构造器(无参或有参),然后执行子类构造器的内容。
super()
必须是子类构造器的第一行代码。- 继承中的构造器:
- 如果父类没有无参构造器,子类必须显式调用父类的有参构造器。
- 如果子类没有显式调用父类构造器,Java 会默认调用父类的无参构造器(前提是父类有无参构造器)。还是那个问题,子类不用父类有参构造,会默认调用父类无参,此时如果父类有有参构造器又没有无参构造器,那就要么添加父类无参构造器,或者显式调用定义好的父类有参构造器
- 默认构造器:
- 如果子类没有定义构造器,Java 会提供一个默认的无参构造器,自动调用父类的无参构造器。
- 如果父类没有无参构造器,则子类必须显式调用父类的有参构造器。