Java 面向对象进阶(1)

static

表示静态,是一个 修饰符,可以修饰成员方法。成员变量

静态变量

static修饰的成员变量

特点

  • 被该类的所有对象共享
  • 不属于对象,属于类
  • 随着类的加载而加载,优先于对象存在

调用方式

  • 类名调用(推荐):ClassName.staticVar
  • 对象名调用: cn.staticVar

static内存图

在遇到Student.teacherName = ""时,就会在方法区中加载该类的字节码文件,同时静态区也出现了 (在堆内存中单独有一个静态存储位置(静态区)),在这里存储着这个类中所有的静态变量。
静态变量时随着类的加载而加载的,优先于对象出现的
在这里插入图片描述
共享的变量一定是用static修饰的

静态方法

static修饰的成员方法

特点

  • 多用在测试类和工具类中
  • Javabean类中很少会用
什么是工具类

帮助做事情,但是不描述任何事物的类

  • Javabean类:用来描述一类事物的类,比如:Student、Cat等
  • 测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
  • 工具类:帮助做事情,但是不描述任何事物的类
    工具类的特点:
  • 类名见名知义
  • 私有化构造方法,为什么要私有化呢?因为这个类没有任何意义
  • 方法定义为静态

调用方式

  • 类名调用
  • 对象名调用

static的注意事项

  • 静态方法只能访问静态变量和静态方法

  • 非静态方法可以访问静态变量和静态方法,也可以访问非静态变量和非静态方法

  • 静态方法中是没有this关键字
    this关键字:
    在类中,定义的非静态成员方法中的形参上,隐藏了一个this,这个this不能手动赋值,而是由虚拟机控制分配内存。谁调用这个方法,那这个this指向的地址就是谁

  • 加载到内存中的时机是不一样的
    静态:随着类的加载而加载
    非静态:与对象有关

静态方法中不能访问非静态的原因

看下方两个static内存图。当调用静态方法时,对于该方法里的变量,都会从静态区获得,因此访问非静态成员变量的时候,会从堆内存中的静态区去寻找,而这个非静态方法不在静态区,因此会找不到该变量
静态方法不能访问非静态变量
当在静态方法中调用非静态方法时,由于非静态方法都隐藏了一个this,这个this是要调用的对象的地址,但是静态方法中是没有这个对象的,因此不能调用
静态方法不能调用非静态成员方法

非静态方法为什么可以访问所有变量

在这里插入图片描述

重识main方法

public static void main(String[] args){

}
  • public:权限修饰符,被JVM调用,权限足够大
  • static:被JVM调用,不用创建对象,直接类名访问;因为main方法是静态的,所以测试类中的其他方法也需要用static修饰
  • void:被JVM调用,不需要给JVM返回值
  • main:主入口的名称,只有main可以被JVM识别
  • 括号里的参数:为了接收键盘录入数据,在以前有用,现在没用了

继承

为什么要有继承
比如:对于学生类和教师类而言,他们都有某些相同的属性和方法,如果之后这样的类越来越多,那么会增加代码的重复性,不好维护。因此可以定义一个Person类,使学生和老师都继承自Person

  • 使用关键字extendspublic class Student extends Person{}
  • 这里Student是子类(派生类),Person是父类(基类或超类)

使用继承的好处

  • 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
  • 子类可以在父类的基础上增加功能,是子类更强大

什么时候使用继承

当类与类之间存在共性,并满足子类是父类的一种,就可以考虑使用继承

继承的特点

Java只支持单继承,不支持多继承,但是支持多层继承

  • 单继承:一个子类只能继承一个父类
  • 不支持多继承:一个子类不能同时继承多个父类
  • 多层继承:子类A继承父类B,父类B继承父类C
  • 每一个类都直接或间接的继承object类
  • 子类只能访问父类中非私有的成员

子类到底能继承父类中的哪些

  • 内存图

  • 内存分析工具

构造方法是否可以被继承

父类的构造方法 不能 被子类继承

成员变量是否可以被继承

无论是私有的还是非私有的 都可以 被继承,但是私有的成员变量不能直接被使用 。如果一定要用,只能使用对应的get/set方法
内存图:
在方法区会把父类的字节码文件进行加载,同时在堆中的内存会一分为二,一半记录子类的成员变量和方法,一半记录父类的成员变量和方法
在这里插入图片描述

成员方法是否可以被继承

能添加到虚方法表中的就可以被继承
java从最顶级的父类中创建了一个虚方法表,把所有经常要用到的方法放在方法表里
什么是经常要用到的方法呢?
①方法不能被private修饰
②方法不能被static修饰
③方法不能被final修饰
在继承中,父类会把自己的虚方法表交给自己的子类
在这里插入图片描述
成员方法被继承的内存图
在这里插入图片描述

继承中成员变量、成员方法、构造方法的访问特点

1. 成员变量访问特点

就近原则,谁近用谁
首先在局部找,找不到就去本类找,本类找不到去父类找,一级一级往上找
如果出现了重名变量怎么办

class Fu {
	String name = "Fu";
}

class Zi extends Fu {
	String name = "Zi";
	public void show(){
		String name = "show";
		sout(name);        // 打印本方法中的name,从局部位置开始往上找
		sout(this.name);   // 打印子类中的name,从本类成员位置开始往上找
		sout(super.name);  // 打印父类中的name,从父类成员位置开始往上找
		// 最多只能调用一个super,也就是最多获得父类的,再高就不行了
	}
}

2. 成员方法的访问特点

  • 直接调用满足就近原则,谁近调用谁
  • 使用super调用,直接访问父类
方法的重写

当父类的方法不能满足子类要求的功能时,需要进行方法重写。
在继承体系中,当子类中出现了和父类中一模一样的方法声明,就意味着该方法被重写
在重写的方法上方,要写上@Overvide

  • 校验重写的方法语法是否正确
  • 加上注解后,如果出现红色波浪线,就代表语法有错误

方法重写的本质
重写的方法会覆盖掉原来虚方法表中的方法
在这里插入图片描述
方法重写注意事项

  • 重写方法的名称、形参列表必须与父类中的完全一致
  • 子类重写方法时,权限子类必须大于等于父类(空着不写< protected < public
  • 子类重写方法时,返回值类型必须小于等于父类的类型
  • 建议:重写方法时尽量和父类保持一致
  • 只有添加到虚方法表中的方法才能被重写

3. 构造方法的访问特点

  • 父类中的构造方法不会被子类继承
  • 子类中的所有构造方法默认先访问父类中的空参构造方法,再访问自己的
    为什么?
    因为子类在初始化的时候可能会访问父类的变量,如果不先访问父类中的构造方法,那么子类获取不到父类中的数据
    怎么调用父类构造方法的
  • 子类构造方法的第一行使用super(),默认在第一行,不写也没关系
  • 如果想调用父类的有参构造,则必须shoudong 写上super()

this、super使用汇总

  • this:理解为一个变量,表示当前方法调用者的地址。在JVM中,this就是一个局部变量
  • super:代表父类存储空间
关键字访问成员变量访问成员方法访问构造方法
thisthis.成员变量 访问本类成员变量this.成员方法(…) 访问本类成员方法this(…)访问本类其它构造方法
supersuper.成员变量 访问父类成员变量super.成员方法(…) 访问父类成员变量方法super(…) 访问父类构造方法

在空参构造中写了this(...)之后,JVM就不会添加super(...)了。当需要给一些数据默认值的时候就会用到this(...)

多态

同类型的对象的不同形态。
表现形式:父类类型 p = 子类对象
使用前提:

  • 有继承和实现关系
  • 有父类引用指向子类对象
  • 有方法重写

多态调用成员的特点

  • 变量调用:编译看左边,运行也看左边
    编译看左边:javac编译代码的时候,会看左边的父类中有没有这个成员变量。如果有,编译成功;反之,编译失败
    运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
  • 方法调用:编译看左边,运行看右边
    编译看左边:javac编译代码的时候,会看左边的父类中有没有这个成员方法。如果有,编译成功;反之,编译失败
    运行看右边:ava运行代码的时候,实际运行的就是右边子类中成员方法

多态调用成员的内存图解

在java中加载字节码文件,永远都是先加载父类,再加载子类
视频讲解:多态
在这里插入图片描述

多态的优势和弊端

优势

  • 在多态形式下,右边对象可以实现解耦合,便于扩展和维护
  • 定义方法时,使用父类型作为参数,可以接受所有子类对象,体现多态的扩展性与遍历

弊端

  • 不能调用子类的特有功能
    解决方案:再变回子类类型就可以了子类类型 d = (子类类型)父类变量
    注意:转换的时候不能瞎转,如果转成其他类型,就会报错
    代码示例:
Animal a = new Dog();
// 要调用子类特有的方法,使用a.不能调用,需要转换为子类类型
Dog d = (Dog)a;
d.methodDog(); // 这时就可以调用子类特有的方法了

// 如果不知道要转成什么类型,可以使用instanceof关键字判断类型
a instanceof Dog: a是不是Dog类型

JDK14新特性:不用在先判断再强转,而是写在一行中。a instanceof Dog d强转之后的变量名为d

引用数据类型的类型转换

  • 自动类型转换
Person p = new Student();
  • 强制类型转换
Student s = (Student)p;
  • 可以解决的问题
    a. 可以被对象转成真正的子类类型,从而调用子类独有的功能
    b. 强转时有可能与真实对象类型不一致报错----使用instanceof可以解决

关于多态的小例子

描述:

根据需求完成代码:
1.定义狗类
属性:年龄,颜色
行为:eat(String something)(something表示吃的东西)、看家lookHome方法(无参数)

2.定义猫类
属性:年龄,颜色
行为:eat(String something)方法(something表示吃的东西)、逮老鼠catchMouse方法(无参数)

3.定义Person类//饲养员
属性:姓名,年龄
行为:keepPet(Dog dog,String something)方法
    功能:喂养宠物狗,something表示喂养的东西
行为:keepPet(cat cat,String something)方法
    功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法

4.定义测试类(完成以下打印效果):
    keepPet(Dog dog,String something)方法打印内容如下:
        年龄为30岁的老王养了一只黑颜色的2岁的狗
        2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
    keepPet(Cat cat,String something)方法打印内容如下
        年龄为25岁的老李养了一只灰颜色的3岁的猫
        3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:

1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keeppet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?

代码实现:

Animal.java
package com.Test2;

public class Animal {
    private int age;
    private String color;

    public Animal() {}

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void eat(String something) {
        System.out.println("吃" +something);
    }

}
Cat.java
package com.Test2;

public class Cat extends Animal{
    public Cat() {
    }

    public Cat(int age, String color) {
        super(age, color);
    }

    @Override
    public void eat(String something) {
        System.out.println(this.getAge() + "岁的" + this.getColor() + "颜色的猫眯着眼睛侧着头吃" + something);
    }

    public void catchMouse(){
        System.out.println("抓老鼠");
    }
}
Dog.java
package com.Test2;

public class Dog extends Animal{
    public Dog() {
    }

    public Dog(int age, String color) {
        super(age, color);
    }

    @Override
    public void eat(String something) {
        System.out.println(this.getAge() + "岁的" + this.getColor() + "颜色的狗两只前腿死死的抱住" + something + "猛吃");
    }

    public void lookHome() {
        System.out.println("看家");
    }
}
Person.java
package com.Test2;

public class Person {
   private String name;
   private int age;
    public Person(){}
    public Person(String name, int age) {
        this.name = name;
        this.age = 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;
    }

    public void keepPet(Animal a, String something) {
        if (a instanceof Dog){
            Dog d = (Dog)a;
            System.out.println("年龄为" + this.age + "岁的" + this.name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的" + "狗");
            d.eat(something);
        } else if (a instanceof Cat) {
            Cat c = (Cat) a;
            System.out.println("年龄为" + this.age + "岁的" + this.name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的" + "猫");
            c.eat(something);
        }else{
            System.out.println("没有这种类型");
        }

    }
}
Test.java
package com.Test2;

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setAge(2);
        dog.setColor("黑");
        Cat cat = new Cat();
        cat.setAge(3);
        cat.setColor("灰");

        Person p1 = new Person("老王", 30);
        Person p2 = new Person("老李", 25);

        p1.keepPet(dog, "骨头");
        p2.keepPet(cat, "鱼");
    }

    public static void keepPet(Animal animal, String something) {

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值