关于Java继承,咱不得不说的4个要点

简介: 关于Java继承,咱不得不说的4个要点

目录


1.继承必须使用extends关键字


2.父类中非私有的属性和方法可以被子类继承


3.构造方法不能被继承,只能通过super关键字去调用


4.调用构造方法并不会创建对象,但是会初始化数据

创建对象的方式1.new关键字 + 构造方法

创建对象的方式2.classloader

创建对象的方式3.反射



微信截图_20230504231622.png


那么大家晚上好,我是今天晚上的主讲老师,我是兔哥。

Java的三大特性,继承、封装和多态。

今天,咱就来聊聊继承。虽然在Java内卷的今天,继承不一定会在面试中问到,但是,这个知识点是我们平时开发代码的重中之重。


因此,掌握Java继承非常有必要。


1.继承必须使用extends关键字


extends关键字代表我们可以继承一个父类,也叫超类。


比如,我有一个A类。


public class A {
    private String name = "A";
}


在新建一个B类,去继承A类


public class B extends A{
}


那么我们就说B类是A类的子类。Java不允许多继承,只允许单继承,但是可以多层继承。

什么是多层继承,就是我在新建一个C类,继承B类


public class C extends B{
}


2.父类中非私有的属性和方法可以被子类继承


我们一般会使用到的访问权限,就说public,protected和private三种。

A类有一个私有的name,子类无法访问,只能自己访问。


验证


1.png


2.png


如果我们换成public


public class A {
    public String name = "A";
}


3.png


都没有报错 , 验证成功!

不过,对于我们希望被子类继承的部分,可以用protected,意思是受保护的,允许传承给子类。

如果我们给name设置成protected,再看B和C类


4.png


一样不会报错。

但是,protected的属性,在其他包不能访问。(报错了)


5.png


Demo.java


public class Demo {
    public static void main(String[] args) {
        String name = new A().name;
        System.out.println(name);
    }
}


上面的代码是会报错的,同时,它也不能在A类的同一个包的子包中被访问到。


6.png


7.png

只能在同一个包中,或者自己的子类中被访问。

如果Demo也是A的子类


public class Demo extends A{
    public void show(){
        System.out.println(super.name);
    }
}


这是允许的,不会报错(哪怕不在一个包中),我们在B类和C类也能访问到name,但是需要用到super关键字。


让我们绕回来,总结下就是父类中非私有的属性和方法可以被子类继承!


如果不设置成public,会造成很多麻烦,其他包调用不到了!所以,父类一般就是给我们继承用的,而不是给你直接在其他地方调用的。


3.构造方法不能被继承,只能通过super关键字去调用


父类的构造方法是不能被继承的,但我们可以通过super关键字去调用。

当我们new一个B类对象,A类的构造器会优先调用。

当我们new一个C类对象,A类和B类的构造器会先后调用。

验证:


public class A {
    protected String name = "A";
    public A(){
        System.out.println("A");
    }
}


public class B extends A{
    public B(){
        System.out.println("B");
    }
}


public class C extends B{
    public C(){
        System.out.println("C");
    }
}


测试


public class Demo {
    public static void main(String[] args) {
       new C();
    }
}


结果


8.png


同样的,父类构造器必须是public或protected,如果用private,子类也无法使用


9.png


10.png


11.png


C类虽然编译不报错,但是运行一定报错。


12.png


父类构造器如果没有声明缺省的,却声明了有参构造器,则子类必须用super关键字调用父类的有参构造。


public class A {
    protected String name = "A";
    protected A(String name){
        System.out.println("A");
        this.name = name;
    }
}


B类报错


13.png


必须手动用super关键字调用一下父类的有参构造才行,哪怕你只是做做样子


public class B extends A{
    public B(){
        super(null);
        System.out.println("B");
    }
}


如果A类有多个构造器,但偏偏就是没有显式声明缺省构造


public class A {
    protected String name = "A";
    protected int price = 100;
    protected A(String name){
        System.out.println("A");
        this.name = name;
    }
    protected A(String name,int price){
        System.out.println("A");
        this.name = name;
        this.price = price;
    }
}


子类随便super一个父类的构造就行,但一定要有。


public class B extends A{
    public B(){
        super(null);
        System.out.println("B");
    }
}


这种也行


public class B extends A{
    public B(){
        super(null,1);
        System.out.println("B");
    }
}


看你具体的情况。


4.调用构造方法并不会创建对象,但是会初始化数据


我们举了这么多例子,不难发现,构造方法的作用只是初始化数据而已。


而我们创建对象是用new关键字配合构造方法的形式,所以我们可能会误认为调用构造方法就会创建对象了。


创建对象的方法,大体有三种:


创建对象的方式1.new关键字 + 构造方法


A a = new A("A",100);


创建对象的方式2.classloader


DiskClassLoader dcl = new DiskClassLoader("D:\\idea-workspace\\j2se\\out\\production\\j2se\\com\\javaxbfs\\bean");
/** 找到对应的class * */
Class<?> aClass = dcl.findClass("com.javaxbfs.bean.A");
/** 获取构造函数 * */
Constructor<?> constructor = aClass.getConstructor(String.class);
/** 用构造函数创建对象 * */
Object o = constructor.newInstance("A");
Method show = aClass.getMethod("show");
show.invoke(o);


DiskClassLoader是我们的自定义类加载器,用来加载磁盘上另外的class。DiskClassLoader的代码附在最后。


创建对象的方式3.反射


其实上面的例子已经用到了反射。


可见,构造方法只是用来初始化数据的,并不会新建一个对象。


而且,构造方法在当前类中,可以在其他构造方法中通过this调用,在子类构造方法中,可以通过super关键字调用(也只能出现在子类的构造方法中)。并且,必须是在方法的第一行。


最后,说说继承的好处,最大的好处,自然是代码的复用,缺点则是增加了耦合。


附上DiskClassLoader的代码:


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class DiskClassLoader extends ClassLoader {
    private String mLibPath;
    public DiskClassLoader(String path) {
        // TODO Auto-generated constructor stub
        mLibPath = path;
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        String fileName = getFileName(name);
        File file = new File(mLibPath,fileName);
        try {
            FileInputStream is = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            try {
                while ((len = is.read()) != -1) {
                    bos.write(len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            byte[] data = bos.toByteArray();
            is.close();
            bos.close();
            return defineClass(name,data,0,data.length);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return super.findClass(name);
    }
    //获取要加载 的class文件名
    private String getFileName(String name) {
        // TODO Auto-generated method stub
        int index = name.lastIndexOf('.');
        if(index == -1){
            return name+".class";
        }else{
            return name.substring(index+1)+".class";
        }
    }
}


相关文章
|
3月前
|
Java 程序员
Java中的继承和多态:理解面向对象编程的核心概念
【8月更文挑战第22天】在Java的世界中,继承和多态不仅仅是编程技巧,它们是构建可维护、可扩展软件架构的基石。通过本文,我们将深入探讨这两个概念,并揭示它们如何共同作用于面向对象编程(OOP)的实践之中。你将了解继承如何简化代码重用,以及多态如何为程序提供灵活性和扩展性。让我们启程,探索Java语言中这些强大特性的秘密。
|
30天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
30天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
37 2
|
30天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
30 2
|
30天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
|
2月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
1月前
|
Java 测试技术 编译器
Java零基础-继承详解!
【10月更文挑战第4天】Java零基础教学篇,手把手实践教学!
39 2
|
1月前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
23 5
|
1月前
|
Java
java继承和多态详解
java继承和多态详解
43 5
|
1月前
|
Java 编译器
【一步一步了解Java系列】:子类继承以及代码块的初始化
【一步一步了解Java系列】:子类继承以及代码块的初始化
26 3
下一篇
无影云桌面