[Java]--一章看懂java封装、继承、多态

🍓作者@ Autumn60
🏄欢迎关注: 👍点赞 🙌收藏 ✍️留言
👲今日鸡汤:每一个决定转身的人,都曾在风里站了很久,只要你想开始,一切都不算太晚,大胆努力的往前走吧,这样耀眼的样子真的很好!

面向对象的三大特性: 封装,继承,多态

一、封装

将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互,就是把类的实现细节进行了隐藏
例如:计算机开机,你只需按开机键开机,至于它是怎么开的你就不需要管了,对外只提供一个开关机的按钮,内部进行了隐藏

在Java当中:主要通过类和访问权限来实现封装,访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:

1.访问修饰限定符(访问权限)

先看完包再返回来看效果更好

private:被private所修饰的属性,只能在当前的类当中所使用,在类的大括号里面使用

default:在你什么都不写的时候,他就是默认的权限,也称包访问权限,只能在同一个包中访问

public:最大的访问权限;

protected下方继承会讲

private是最小的访问权限,可以对类进行封装,public是最大的访问权限

图例:

1.1包

概念:Java当中组织类的一种方式

在java当中包就是对类,接口等的封装机制的体现,是一种对类或者接口很好的组织方式
白话:就是文件夹组织文件的

导入包中的类

  1. 三种导包方式:

1.

在java当中提供了很多现成的类供我们使用,可以使用import java.util.Date导入在Java.util这个包中的Date类

2.

import java.util.*; 这里的 * 就是通配符;
哪一个类调用,这里的 * 就导入哪个类的包
[建议]统配符 * 号非必要不要使用

3.

import static java.lang.Math. * //解释:导入一个静态的Math的方法
这样写,就可以不用加Math了,默认你会直接调用Math里的所有函数
直接可以写pow(),否则就需要Math.pow(),很少用

【注意事项】

在一个工程中,不允许存在相同名称的类,只要处在不同的包中即可
白话理解 : (在同一个文件夹中,不允许出现两个相同名字的文件)
总结:Java当中的包就是组织类的
和C语言当中的include<>导包方式差不多

2.自定义包:

在IDEA当中右键单击直接创建一个包

包名一般来说是公司的域名反写,比如说:com.wangyi.www

创建好之后:

package:声明当前的java文件在哪个包当中

【注意事项】

  • 在文件的最上方加上一个 package 语句指定该代码在哪个包中.

  • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例com.wangyi.www)

  • 包名要和代码路径相匹配. 例如创建 com.wangyi.www 的包, 那么会存在一个对应的路径 com/wangyi/www 来存储代码

  • 如果一个类没有 package 语句, 则该类被放到一个默认包中.


3.static修饰成员变量

成员变量分为两种:

1.一种是普通成员变量

2.一种是被Static修饰过的成员变量:也称为类变量/静态成员变量,是所有对象公用的一个对象

共同点:在类的内部,在方法的外部
不同点:静态方法变量是被Static修饰了

静态成员的访问

Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。

【静态成员变量特性】

1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 类变量存储在方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
类的静态成员变量,通过类名访问,也就是说:这个静态成员变量不属于对象
所以说new不new对象,和调用静态成员变量没有关系
class Student {
     //private修饰的成员变量
    private String name;
    private String gender;
    //静态变量
    public static String classRoom = "1234";
    public Student(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }
    public void printStudent() {
        //打印成员变量
        System.out.println(this.name + "---" + this.gender);
    }
}
public class Code3_21 {
        public static void main(String[] args) {
            //创建成员变量并给赋值
            Student s1 = new Student("张三","男");
            Student s2 = new Student("李四","女");
            Student s3 = new Student("王麻子","男");
            //  也可以通过对象访问:但是classRoom是上面三个对象共享的
            //  使用对象去调用静态方法
            //System.out.println(s1.classRoom);
            //使用类名调用静态方法
            System.out.println(Student.classRoom);
            //调用s1成员方法
            s1.printStudent();
        }
}
静态方法的内部,是不能调用非静态的方法的.
因为静态方法不依赖于对象,但是非静态的方法依赖对象!

静态方法特性

1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 不能在静态方法中访问任何非静态成员变量
4.在静态方法内部不能使用this,静态的方法里面,只能用静态的属性
5.Static只能修饰成员变量不可以修饰局部变量

4.静态成员的初始化

代码块:由{}括号括起来的就是代码块

  1. 静态代码块

static {
this.name = "zhangsan";
this.age = 12;
this.sex = "男";
System.out.println("调用实例化代码块");
}
  1. 实例(构造)代码块

构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块构造代码块一般用于初始化

代码示例:

class Student{
    //实例成员变量
    private String name;
    private int age;
    private String sex;
    public Student() {
        System.out.println("调用构造方法");
    }
    //实例代码块/构造代码块
    {
        this.name = "zhangsan";
        this.age = 12;
        this.sex = "男";
        System.out.println("调用实例化代码块");
    }
    public void show(){
        System.out.println("name: "+name+" age: "+age+" sex: "+sex);
    }
}
public class Code3_21 {
    public static void main(String[] args) {
        Student st = new Student();//调用构造方法;
        st.show();                 //调用成员方法;
    }
}
//执行结果:
//        实例化构造代码块
//        调用构造方法
//        name: zhangsan age: 12 sex: 男

执行顺序:

  1. 普通代码块:

{
System.out.println("普通代码块,没有实际的意义");
}

【注意事项】

1.静态代码块不管生成多少个对象,其只会执行一次(只要是调用类就会被执行)
2. 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3. 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
4. 实例代码块只有在创建对象时才会执行

对象的打印

也可以通过重写一个toString,来改变返回值
    @Override//下章会介绍
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
public class Code3_21 {
    public static void main(String[] args) {
        Student st = new Student();//调用构造方法;
        System.out.println(st);
    }
}
此时返回的不会是第上图中的地址,而是

二、继承

1.什么是继承

继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行 扩展,增加新功能,这样产生新的类,称 派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。

图例:

上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为 父类/基类或超类,Dog和Cat可以称为Animal的 子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。

创建一个类,继承父类,形成新的类 ,这时的子类对象由父类成员和子类成员构成

图例:

例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后把共性放在父类当中,采用继承的思想来达到共用。主要用来解决共性的抽取,实现代码复用

【注意事项】

1. 子类会将父类中的成员变量或者成员方法继承到子类中了
2.子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
2.1白话理解:子类继承父类之后,如果没有特有的东西,继承父类是没有意义的.
3.在Java当中是不能实现多继承的

2.语法

修饰符 class 子类 extends 父类 {
// ...
}

可以使用extends 来继承父类

示例代码:

class An {
    public int a = 999;
}
    class Bn extends An {        //子类Bn通过关键字extends继承父类An
        public int b;
}

3.父类成员的访问:

class An {
    public int a = 999;
}
    class Bn extends An {
        public int a = 111;
        public int b;
        public int c;
        public void Eva() {
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);
        }
    }
public class Date3_22 {
    public static void main(String[] args) {
        Bn b = new Bn();    //new一个子类类型的对象
        b.Eva();            //通过对象去调用Eva方法访问子类成员方法
    }
}
//输出结果为:
//            111
//            0
//            0
当父类和子类里面共同有一个成员变量时,会是怎么输出呢?

此时我们发现:当父类和子类共有一个成员变量时,会优先输出子类自己的,那么我们如何去在子类当中调用父类的成员a呢?

【注意事项】

在子类方法中 或者 通过子类对象访问成员时

1. 如果访问的成员变量子类中有,优先访问自己的成员变量。
2. 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
3. 如果访问的成员变量与父类中成员变量同名,则优先访问自己的
总结: 成员变量,自己有先访问自己的,没有再从父类中找;

4.super关键字:

在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可

白话理解:来访问子类对象中从父类继承过来部分的一个引用

 public void Eva() {
            //通过用关键字super.a  //来打印子类对象中从父类继承过来的a
            System.out.println(super.a);
            System.out.println(a);      //输出子类中的a
            System.out.println(c);
        }
//此时输出结果为
//            999
//            111
//            0

作用:增加了代码的可读性:上面第二上代码解读:打印父类成员变量a;

【注意事项】

1.这里访问的是:子类对象中从父类继承过来部分, 这里没有产生父类对象,所以不是父类对象的引用!
2. 只能在非静态方法中使用
3. 在子类方法中,访问父类的成员变量和方法
4.必须在子类当中使用,这样才能调用父类的属性或者方法

5.子类构造方法

概念:当父类有构造方法的时候,那么子类要先帮助我的父类进行构造,通过super()来进行构造

构造方法代码示例:

class An {
    public int a;
    public int b;

    public An(int a, int b) {                   //构造方法
        this.a = a;
        this.b = b;
    }
}
    class Bn extends An {
        public int c;

        public Bn(int a, int b,int c) {         //构造方法
            super(a, b);                //给父类的a,b赋值
            this.c = c;                 //自己的c赋值
        }

        public void Eva() {
            System.out.println(super.a);
            System.out.println(b);
            System.out.println(c);
        }
    }
public class Date3_22 {
    public static void main(String[] args) {
        Bn b = new Bn(11,22,33);    //调用带三个参数的构造方法
        b.Eva();            //通过对象去调用Eva方法访问子类成员方法
    }
}
总结: 在构造子类对象时,先调用父类的构造方法,将父类继承部分构造完整,再调用子类自己,将子类新增成员初始化完整 (在帮助父类构造时,super必须放在第一行)

6.this 和super 的区别

相同点
1. 都是Java中的关键字
2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
不同点
1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承 下来部分成员的引用
白话:一个是当前对象引用,一个是子类从父类继承下来部分成员的引用
2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同 时在构造方法中出现
4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

图例:


7.再谈初始化

class Animal {
    public String name;
    public int age;

    static {            //静态代码块
        System.out.println("Animal静态代码块!");
    }

    {                   //构造代码块
        System.out.println("Animal构造代码块!");
    }

                        //构造方法必须和类名一致(可以构成重载),//不带参数的构造方法,构造方法
    public Animal() {
        System.out.println("不带参数的构造方法!Animal");
    }

                        //带两个参数的构造方法
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
                        //成员方法
    public void eat() {
        System.out.println(this.name + " 正在吃饭!");
    }
}
                        //继承了Animal的Dog类
class Dog extends Animal {
    String fad;

    static {            //子类静态代码块
        System.out.println("Dog静态代码块!");
    }

    {                   //子类构造代码块
        System.out.println("Dog构造代码块!");
    }
                        //子类构造方法
    public Dog() {
        System.out.println("不带参数Dog!");
    }
                        //带两个参数的子类构造方法
    public Dog(String name, int age, String fad) {
        super(name, age);
        this.fad = fad;
    }
                        //子类成员方法
    public void bark() {
        System.out.println(this.name + " 正在旺旺旺!!");
    }
}
public class Code3_22 {
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}
/*输出结果
            Animal静态代码块!
            Dog静态代码块!
            Animal构造代码块!
            不带参数的构造方法!Animal
            Dog构造代码块!
            不带参数Dog!
*/

图例解释 :

上图代码执行顺序:

【注意事项】静态代码块只会执行一次


8.protected修饰限定符


9.final 关键字

fifinal关键可以用来修饰变量、成员方法以及类。

1. 修饰变量 为 表示常量(即不能修改)
final int a = 10; //被修饰过后为常量10
a = 20; // 编译出错 //常量不能被赋值
// 如果定义时没有被初始化
2. 修饰类:表示此类不能被继承
final public class Animal {
        //...
}
public class Bird extends Animal {
        //...
}
// 编译出错

10.继承和组合

例如电脑是由:控制器, 运算器 , 存储器 , 输入设备 , 输出设备构成。而这些都是它的功能,

把这些组合在一起就是一台电脑;这就是组合.

组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法

继承和组合的优缺点:

如果没看懂请点击:深入理解Java中的组合和继承-HollisChuang's Blog


[继承小练]本题会输出什么?

class Base{
  public Base(String s){
    System.out.print("B");
  }
}

public class Derived extends Base{
  public Derived (String s) {
    System.out.print("D");
  }
  public static void main(String[] args){
    new Derived("C");
  }
}

正确答案解析:http://t.csdn.cn/phHOV


三、多态

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
同一件事情,发生在不同对象身上,就会产生不同的结果

白话理解:就是吃饭:狗吃狗粮,猫吃的是猫粮


1. 多态实现条件

在java中要实现多态,必须要满足如下几个条件,缺一不可:

1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
1.1重写/覆盖(Override):
重写是子类对父类非静态、非private修饰,非fifinal修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法
1.方法名称相同
2.参数列表相同
3.返回值类型相同

要点:子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致

代码示例:

class Animal {
    public String name;
    public void eat() {
        System.out.println("正在吃饭");
    }
}
class Dog extends Animal {
    @Override           //此时eat就构成了重载
    public void eat() {
        System.out.println(this.name + "正在吃狗粮");
    }
}
public class Code3_23 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小狗";
        dog.eat();  //输出结果是小狗正在吃狗粮
    }
}

1.重写与重载(overload)对比:

1.方法名称相同
2.参数列表不同
3.返回值类型相同

【注意事项】

1. 被private修饰过的方法不能进行重写,因为private的权限是同一包中的同一类,而重写是在同一包 中的不同了类当中进行重写
2. 被static修饰过的方法不能进行重写,因为被static修饰过的方法它是类的,而重写是针对某个对象 的某个方法进行重写的
3. 子类的访问修饰限定符一定要大于等于父类的修饰限定符
4. 被final修饰过的方法不能进行重写, 被final修饰过的方法叫做【密封方法】
5. 构造方法不能被重写
6. 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就 会编译报错, 提示无法构成重写. (提示的作用)
7. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的(下方代码示例)

代码示例:

class Animal {
    public String name;         //定义父类成员
    public Animal eat() {       //返回类型是Animal类型
        System.out.println("正在吃饭");
        return null;
    }
}
class Dog extends Animal {
    public Dog(){
        super();
    }
    @Override           
    public Dog eat() {      //返回类型是Dog类
        System.out.println(this.name + "正在吃狗粮");
        return null;
    }
}

1.2向上转型

实际就是创建一个子类对象,将其当成父类对象来使用。

白话理解:用父类引用 来 引用子类对象

代码示例:

Animal animal = new Dog();
//animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。

图例1

白话:计算机就是电子产品

图例2

白话:狗就是一个动物

1.3运行时绑定/动态绑定:

❀1.4如何发生向上转型

1. 直接赋值

图例

2. 方法传参

图例

3. 方法返回

图例

多态代码示例:
class Animal {
    public String name;
    public int age;

    public void eat() {       //返回类型是Animal类型
        System.out.println("正在吃饭");
    }
}
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println(this.name+ " 正在吃狗粮!");
    }
}
class Cat extends Animal {
    public void eat() {
        System.out.println(this.name + " 正在吃猫粮!");
    }
}
public class Code3_23 {
    //只有一个isFunc方法
    //当animal这个引用所引用的对象不同时,所产生的的结果也不同
    //这个就是多态
     static void testFunc(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println("System.out.println("分割线====================分割线");");
        Cat cat = new Cat();
        testFunc(cat);
    }
}
//输出结果

【注意事项】

多态:

同一个引用,调用同一个方法,当这个引用所引用的对象不一样时,它在里面所呈现的结果也是不一样的!
前提:传过来的一定是子类
发生动态绑定需要满足两个条件:
1.发生了重写
2.是通过父类引用来调用这个重写的方法

以上就是多态


1.5向下转型

例子:

代码如下:且需要强转,且不安全

Cat cat=(Cat)new Animal();

代码小练习:验证年龄_牛客题霸_牛客网 (nowcoder.com)


  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Autumn60

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

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

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

打赏作者

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

抵扣说明:

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

余额充值