包、继承、多态

目录

一、包(package)

创建包

import与package的区别

包的访问权限控制

二、继承

概念

super关键字

三、protected关键字

四、多态

向上转型

动态绑定

方法重写(override)

在构造方法中调用重写的方法

向下转型


一、包(package)

包,即package,其实就是存放了很多类。

存着就有要用的时候,所以我们可以用import来引入类

这里带来一个注意点,import 是引入类,不是引入包。

例如util是一个包,但是Date是一个类

 

 第一种引入包就报错了,第二种引入类不报错


再看这种方式

这个就是导入util包中所有的类,但只有你用到包中某个类时,才会自动引用。


创建包

首先要注意包名要小写

我们右击src

在写包名,我们可以用 '.'来表示下一级目录

如果这样输入:

 就会如下创建好文件夹


import与package的区别

上面说过,import是导入包中的类,所以区别就来了

package是为了保证类的唯一性,将类导入到包中,供以后使用

import导入包类后,可以区别两个同名的但不同包的类。

这里就引出了类的唯一性,举个例子:

我这里有一个TestDemo的java文件,我还想创建一个同名的类,可以在包中实现。

这两个是同名但不同包的类,在调用时只要说明包即可


包的访问权限控制

前面学过public和private,但有时也不加

如果在包中的类不写public或者private,比如上面包中的Test

public class Test {
    int val = 10;
}

我在包外的TestDemo中是无法调用它的。

val标红,说明语法错误,不能访问其他包中不带public的变量或方法。

 

二、继承

概念

继承,即对共性的抽取,来看例子:

class Dog{
    public String name;
    public int age;

    public void eat(){
        System.out.println(name + "eat");
    }
}
class Bird{
    public String wing;
    public String name;
    public int age;


    public void fly(){
        System.out.println(name + "fly");
    }

    public void eat(){
        System.out.println(name + "eat");
    }

}

看这两个类,都有共同的name、age变量和eat()方法

所以我们可以对共性抽取,写一个Animal类,包含这三个元素

再让dog类和bird类继承(extends),就可以包含这三个元素,使代码更简洁

class Animal{
    public String name;
    public int age;
   
    public void eat(){
        System.out.println(name + "eat");
    }
}

class Dog extends Animal{
    
}

class Bird extends Animal{
    public String wing;
   
    public void fly(){
        System.out.println(name + "fly");
    }
}

这样就完成了继承,Animal就是父类,Dog和Bird就是子类

我们来实例化dog类,看看有没有继承元素。

public class Main{
    public static void main1(String[] args) {
        Dog dog = new Dog("hh", 18);
        dog.name = "hh";
        System.out.println(dog.name);
        dog.eat();
    }
}

运行结果: 

继承注意点:

1. 父类中被 private、static 修饰的方法与变量不能被继承

2. 一个类如果被 final 修饰也不能被继承

3. 一个子类只能继承一个父类,Java不能多继承


super关键字

从上面的dog类可以看出,dog类继承了animal类的所有元素

但是如果我想用animal的变量呢?

这里引出super关键字,用这个关键字就可以调用父类的方法或变量

这里我们把父类animal的eat方法区别开来,再修改一下dog类

class Animal{
    public String name;
    public int age;
   
    public void eat(){
        System.out.println("ani::eat");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("dog::eat");
    }
    public void test(){
        eat();
        super.eat();
    }
}
public class test {
	public static void main(String[] args) {
		Dog dog = new Dog();
		dog.test();
	}
}

来看结果:

这样super的作用就体现了。


接下来我们看super调用构造方法

我们在父类animal中写一个带参的构造方法

这里引出注意点:

任何一个子类构造方法第一句都会先访问父类无参构造方法

如果父类有带参构造,子类必须帮助父类实现构造方法

class Animal{
    public String name;
    public int age;

    public void eat(){
        System.out.println(name + "ani::eat");
    }

    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
}

class Dog extends Animal{
    public Dog(String name, int age) {
        //这里只帮父类初始化了,子类自己没初始化
		super(name, age);
	}
	public void eat(){
        System.out.println(name + " dog::eat");
    }
    public void test(){
        eat();
        super.eat();
    }
}

这是我们调用一下:

public class test {
	public static void main(String[] args) {
		Dog dog = new Dog("haha", 18);
		dog.test();
	}
}

从结果可以看出,父类animal初识化了,但是子类并没有。

这个haha是父类的name,父类、子类共用这个haha。


如果我们给子类一个同名的变量name

class Dog extends Animal{

	public String name;//和父类同名的变量

    public Dog(String name, int age) {
		super(name, age);
	}
    
	public void eat(){
        System.out.println(name + " dog::eat");
    }
	
    public void test(){
        eat();
        super.eat();
    }
}

此时再给这个name赋个值

public class test {
	public static void main(String[] args) {
		Dog dog = new Dog("haha", 18);
		dog.name = "jj";
		dog.test();
	}
}

来运行一下:

  

这里发现dog的name已经被改了。

由此得出:在子类和父类同名变量时,子类中的变量优先,除非使用super

来加个super试试,把dog类的eat中name加上super

    public void eat(){
        System.out.println(super.name + " dog::eat");
    }

 由此可以发现,通过super,又重新调用了父类的name。

 

三、protected关键字

我们了解,private 能将数据封装起来,但是子类不能访问

但是用 public 不能达到封装的效果。

所以就有了 protected 关键字,它的效果夹在他们中间

对于类的调用者来说, protected 修饰的字段和方法是不能访问的

对于类的 子类 和 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的

来个例子:

对于子类来说:

class Animal{
    //...
    protected int a = 0;
    //...
}
class Dog extends Animal{
    //...
    public void test(){
        System.out.println(super.a);
    }
}

这样就可以访问。


对于不同包: 

//包的默认访问权限——只能在当前包中使用
public class Test {
    int val = 10;//其他包外的子类不能访问
    protected int count = 99;//其他包外的子类可以访问
}
class Test2 extends Test {//继承了别的包的类
    public static void main(String[] args) {
        Test2 test2 = new Test2();
        //System.out.println(test2.val);错误
        System.out.println(test2.count);//但是protected的count就可以
    }
}

结果如上。


来个总结:

   

四、多态

向上转型

我们实例化一个dog类,也可以通过animal的引用指向dog类的对象

//向上转型,即父类引用 引用子类对象
Animal animal = new Dog("haha", 19);

向上转型也可以作为函数参数

    public static void func(Animal animal) {
        
    }

    public static void main(String[] args) {
        //第二种情况,用函数
        Dog dog = new Dog("hehe",20);
        func(dog);
    }

向上转型也可以用作返回值的情况:

    public static Animal func2(){
        Dog dog = new Dog("hehe",20);
        return dog;
    }    
    public static void main(String[] args) {
        //第三种情况,返回值
        func2();
    }

动态绑定

当父类与子类中出现同名的方法会怎么样呢?

class Animal{
    public String name;
   
    public void eat(){
        System.out.println(name + " ani::eat");
    }
    
    public Animal(String name){
        this.name = name;
    }
}

class Dog extends Animal{
    public Dog(String name) {
		super(name);
	}
    
	public void eat(){
        System.out.println(name + " dog::eat");
    }
}

public class test {
	public static void main(String[] args) {
		Animal animal = new Dog("11");
		animal.eat();
	}
}

我们可以发现,虽然通过父类的引用,但是调用了子类的方法

因此在 Java 中,调用某个类的方法,究竟执行了父类还是子类的代码

要看究竟这个引用指向的是父类对象还是子类对象.

这个过程是程序运行时决定的(而不是编译期), 因此称为动态绑定. 


方法重写(override)

上面的 eat 方法,就是典型的方法重写

重写即子类重新把父类的方法写一遍。

这里再整理一下重写规则:

1. 重写和重载完全不一样. 不要混淆

重载,在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数个数/类型

重写,在子类中创建,参数列表、返回类型必须与被重写的方法相同

2. 普通方法可以重写, private、static、final 修饰的静态方法不能重写

3. 重写中子类的方法的访问权限不能低于父类的方法访问权限.

比如这种就不符合规定:

class Animal{
    protected void eat(){
        System.out.println(name + " ani::eat");
    }
}

class Dog extends Animal{
	private void eat(){
        System.out.println(name + " dog::eat");
    }
}

4. 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外).


这里还有一个注意点:

class Animal{
    public String name = "heihei";
}

class Dog extends Animal{
	public String name = "haha";
}

public class test {
	public static void main(String[] args) {
		Animal animal1 = new Dog();
        System.out.println(animal1.name);
	}
}

这样的结果如何呢?

animal的类型毕竟是Animal类型,父类引用只能访问自己父类成员和方法

来总结一波:

向上转型,但自己还是父类类型,只能引用自己的方法与变量

但如果有重写方法,优先用子类重写的方法。


在构造方法中调用重写的方法

class Animal{
    public String name;

    public void eat(){
        System.out.println(name + "ani::eat");
    }

    public Animal(String name, int age){
        eat();
        this.name = name;
        this.age = age;
    }
}

class Dog extends Animal{
    public Dog(String name){
        super(name);
    }

    @Override
    public void eat(){
        System.out.println(name + "狼吞虎咽");
    }
}    

public class Main{
    public static void main(String[] args) {
        Dog dog = new Dog("haha");
    }
}

这里dog实例化,会先调用父类的构造方法

父类的构造方法内有父类自己的eat方法, 所以这里的eat会用哪个呢?

结果是子类的,这里也发生了动态绑定。


向下转型

有向上,那也有向下,

我们再来一个 Bird 类来看例子:

class Bird extends Animal{
    public void fly(){
        System.out.println(name + "fly");
    }

    public Bird(String name, int age){
        super(name, age);
    }
}

public class Main{
    public static void main(String[] args) {
        //向下转型
        Animal animal = new Bird("haha", 20);
        Bird bird = (Bird) animal;
    }
}

把animal对象转成bird对象,就是向下转型

但是又有特殊情况:如果当时创建的是Dog类的对象,就没有fly的方法:

    public static void main(String[] args) {
		Animal animal = new Dog("haha");
        Bird bird = (Bird) animal;
        bird.fly();
	}

这里new了一个Dog,我们向下转型再调用fly方法就会报错。

所以要判断一下之前的对象,用instanceof

    public static void main(String[] args) {
        //向下转型
        Animal animal = new Dog("haha");
        Bird bird = (Bird) animal;

        //不是所有动物都会飞,要判断
        if (bird instanceof Bird){
            bird.fly();
        }
    }

 

码字不易,只求大佬一赞👍。欢迎大家指正,一起加油学习。🎉

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丶chuchu丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值