Java面向对象-三大特性(封装、继承、多态)-包和导包

Java面向对象-三大特性(封装、继承、多态)-包和导包,第1张

Java面向对象-三大特性(封装、继承、多态)-包和导包

目录

一、封装

1.1.为什么要使用封装?

1.2.封装的初次尝试

1.2.1.思路

1.2.2.private修饰符

1.2.3.private的使用

1.3.封装的概念

1.4.封装的优点

1.5.JavaBean规范

二、继承

2.1.继承的概念

2.1.1.什么是继承?

2.1.2.为什么要有继承?

2.2.继承的使用 

2.2.1.怎么使用继承

2.2.2.注意事项

2.2.3.单继承的优缺点

2.2.4.面向对象的设计原则之一

2.3.Object类

2.3.1.Object类的方法

2.4.super

2.4.1.子类的实例化

2.4.2.this和super

2.4.3.super.的使用

2.4.4.super()的使用

2.4.5.注意事项

2.5.继承的内存结构

2.5.1.方法区和永久区的区别

2.6.区分同名总结

2.6.1.默认情况

2.6.2.需要特定指定

2.6.重写

2.6.1.判断重写

2.6.2.重写的特征

2.6.3.不能被重写的方法

2.6.4.重写toString

2.6.5.重写equals方法

2.6.6.注意

2.7.疑惑

2.7.1.为什么父类要写这样无用代码

三、多态

3.1.为什么要使用多态?

3.2.多态的表现形式

3.3.多态的前提

3.4.多态的调用

3.4.1.调用实例成员

3.4.2.调用方法

3.5.多态的注意事项

四、包和导包

4.1.包

4.1.1.包的使用

4.1.2.包的作用

4.2.导包

4.2.1.什么时候不需要导包?

4.2.2.导包的使用

4.2.3.导包方式

4.2.4.总结

五、权限修饰符

5.1.权限修饰符的分类

5.1.1.pubilc

5.1.2.protected

5.1.3.default

5.1.4.private

5.1.5.注意事项

5.1.6.修饰符总结表格

5.2.权限修饰符的使用

5.2.1.父类自己创建对象

5.2.2.子类创建对象

六、final修饰符

6.1.final的使用

6.1.1.常量(变量)语法

6.1.2.方法语法

6.2.final的注意事项


一、封装 1.1.为什么要使用封装?

        我们创建一个表示Person的JavaBean模板类|实体类

class Person{
	//字段
	public String name;
	public int age;	
	//空构造
	public Person() {
			
	}
}

        我们单纯的看这个Person类,可能没有什么问题?
        但是实际上,我们创建一个Person类的实例,之后,我们给这个对象的age属性赋值-15或是200,编译运行的时候也是不报错的。
        也就是说,这样的在代码中是合适的,但是代码是要反应现实的,放在现实世界中是不合理的。这是代码逻辑和显示逻辑的不吻合。
        不仅如此,我们在修改对象的属性值的时候,也只需要通过对象名.属性名就可以进行修改了,这看起来没什么,很容易就修改了。但是如果这个属性是你yhk里的余额呢?是不是突然觉得自己的小钱包已经不安全了。
        我们接下来就尝试解决一下这个问题吧。

1.2.封装的初次尝试 1.2.1.思路

        我们首先解决属性修改过于容易的问题。
        仔细回想一下,我们经常修改属性的值是不是在main方法中,也就是说,在方法中,我们可以对属性的值进行修改。
        如果,我们把对属性的值的修改放到方法中,让使用这个Person类的用户都通过方法进行修改,而不是通过对象名.的方式进行修改。
        这个方法一定是pubilc修饰的,只有pubilc修饰的方法才可以让用户使用。

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

        此时,我们发现,采用方法来修改属性的同时,我们还可以再方法中对用户输入的值进行判断,当用户输入的值不满足条件的时候,赋值失败。
        这样,就同时也解决了Person类中的age可以为负数的问题。

        但是,我们虽然设置了方法来修改属性值,但是实际上,还是可以通过对象名.的方式来修改,这是因为我们的属性值是使用pubilc修饰的,对外界来说,是可见的。
        我们可以通过private修饰符来解决这个问题。

1.2.2.private修饰符

        私有的,只能在类中访问
        成员修饰符,只能修饰成员,不能修饰局部

        对于私有的属性而言,需要配合一对公共的访问方式来使用
        设置器        setter:成员方法,为私有成员设置属性值
        访问器        getter:成员方法,获取私有属性的值

1.2.3.private的使用

        第一步,我们需要把Person类成员变量中使用pubilc修饰的实例变量都修改为private修饰符。这回让外界不能再通过对象名.的方式修改属性值。

class Person{
    private String name;
    private int age;
}

         第二步,我们需要添加对外的方法,放外界通过我们指定的方法来修改值以及获取我们对象中的值。

class Person{
    private String name;
    private int age;

    //构造器
    public Person(){}

    //设置器
    public void setAge(int age){
        if(age<=0 || age>150){
            return;
        }
        this.age = age;
    }

    //访问器
    public int getAge(){
        return this.age;
    }
}
1.3.封装的概念

        隐藏内部的实现细节,对外提供公用的访问方式

1.4.封装的优点

        1.提高安全性
        2.提高代码复用性

1.5.JavaBean规范

        JavaBean表示一系列的实体类|模板类

规范:

        1.类是公共的
        2.至少提供一个空构造
        3.属性私有化
        4.提供一对公共访问方式
        5.重写toString、equals方法

二、继承 2.1.继承的概念 2.1.1.什么是继承?

        所谓的继承,通俗的讲,就是子承父业。
        就像我所提及的类,是把各个对象的共同点抽象出来,变成一个类。但是实际上,就像上面所说的狗类一样,里面的拉布拉多也是可以变成一个类的,所以,我们实际上可以把拉布拉多、哈士奇这些类中的共同性的东西在抽取出来,变成一个类,然后让拉布拉多等类继承这个共性的类,这样,可以省略很多重复性质的代码。

2.1.2.为什么要有继承?

        提高代码的复用性,省略编写重复性代码的行为。

2.2.继承的使用  2.2.1.怎么使用继承

语法格式:            class A extends B {}

被继承的类:        父类、基类、超类

继承父类的类:    子类、派生类

2.2.2.注意事项

        1.子类一旦继承父类,有权使用父类的非静态内容
        2.在Java中一个class只允许继承一个class,不允许同时承多个类(单继承)
        3.因为是单继承,所以,Java中的继承对代码复用性的提升并不高,我们推荐使用依赖。
        4.当我们使用继承的时候,我们可以让父类再继承一个类,这样就可以变成伪多继承了。
        5.但我们使用伪多继承的时候,一般最多不超过三层继承。

2.2.3.单继承的优缺点

优点

        简单,不够灵活

缺点

        不便于后期维护

2.2.4.面向对象的设计原则之一

        1.开闭原则:对修改关闭;对扩展开放
                子类中可以扩展子类中独有的内容

2.3.Object类

        当一个类没有规定继承的情况下,所有的类都默认继承了Object类(超类、基类)。
        我们可以类比成中国人都是炎黄子孙。所以Object类是所有类的父类

2.3.1.Object类的方法

        clone():克隆模式        一般可实现Cloneable结合使用
        finalize():GC垃圾回收器回收时需要调用的方法
        hashcode():获取当前对象的hash值
        getClass():获取某个指定类的对应的Class对象
        toString():获取当前对象的内容【常用】
        equals():比较两个对象是否相等【常用】
        wait、notify、notifyAll:在多线程中使用

2.4.super 2.4.1.子类的实例化

        创建子类对象:先创建父类对象,在创建子类对象。
        在这样创建的父类对象在内存中可以说是包含在子类对象所属内存空间中的,因为它没有一个确切的对象名来指向自身。
        本质上,当我们一个类继承了另外一个类的时候,我们创建这个类的实例化对象的时候,就会先创建父类对象,再创建子类对象。

2.4.2.this和super

        super类似于this,区别在于,this中保存的是自身的内存地址;但是super中保存的是对象自身实例化的时候依据父类开辟的内存空间地址。
        this:指代当前new的对象
        super:指代父类对象

2.4.3.super.的使用

        当父类,子类出现了同名方法的时候,我们就需要使用super.来特指调用父类的方法。

不可省略

        避免(父类和字类出现了同名变量和方法)

可省略

        剩下的情况都是可以省略的(省略的位置既可以是this又可以是super)

2.4.4.super()的使用

        super()的使用和this()的使用大同小异,使用方式相同,只是指向的引用不同
        创建子类对象的时候,默认会在子类的构造器中的第一行存在super()调用父类的构造器,子类在被创建的时候必须要保证父类被加载。

2.4.5.注意事项

        1.在构造器中,this()和super()只能存在一个。这是因为super()和this()都需要写到构造器的第一行,有冲突。
        2.当一个构造器中,存在了this()的时候,那么这个构造器中的super()就不存在了,不会被默认调用。
        也就是说,当我们出现this()和super()需要同时存在的时候,省略super
        3.在静态方法中,不能使用this
        这是因为,静态方法是在类被加载的时候加载,而我们的this和super则是在创建对象的时候才会执行。因此,在类加载的时候,会发现,this和super找不到。

2.5.继承的内存结构

        在内存中,当我们执行到某一个实例化代码的时候,会直接先通过本身的class类中定义的成员变量开辟空间并初始化;之后发现继承的时候,会根据自己父类的成员变量开辟一块新的空间并进行初始化。
        这个变量在开辟空间的时候,会定义一个this指向自己的内存地址;同时也会定义一个super指向自己依据父类开辟的内存空间。
        至于两者的方法,则是保存在方法区中,有且只有一份。

2.5.1.方法区和永久区的区别

        永久区是一个概念,是我们将永久区的概念实现了之后,取名为方法区。

2.6.区分同名总结

        当我们的局部、子类中、父类中有同名变量或方法的时候

2.6.1.默认情况

        就近原则,现在局部中寻找,找不到就在所属的子类中寻找,最后再去父类中寻找。

2.6.2.需要特定指定

        找子类:this.子类实例变量/实例方法(没有同名则可以省略)
        当我们具有同名问题的时候,如果我们需要使用子类中的方法,此时,我们就可以通过this.来指定我们要使用的是子类的方法;
        找父类:super.父类的实例成员/实例方法(没有同名则可以省略)
        如果我们要使用父类的方法,我们就需要通过super.来跳过子类寻找,直接找到父类的该方法。

2.6.重写

        当我们的父类中定义的方法不满足字类的需求,比如,猫猫和狗狗都会叫,但是两者的叫声不同,对于父类而言,他只定义了猫猫跟狗狗叫,却没有明确的表明,猫猫是喵喵叫;狗狗是汪汪叫,这就造成了父类有方法,但是不满足字类需求的情况发生。
        所以,对于这种情况而言,我们可以通过在字类中重写方法来解决。

2.6.1.判断重写

        判断一个方法是否被重写        

        方式一:我们可以通过编译器左边的符号进行判断,重写方法的左侧符号是一个字母o加上一个向上的箭头。

        方式二:对于重写的方法,我们需要在方法上方写一行代码@Override

2.6.2.重写的特征

==        方法签名必须一致

<=        返回值的类型
        若返回值的类型为基本数据类型或void,必须保持一致
        若返回值的数据类型为引用数据类型,则要求子类中的返回值类型必须小于或等于父类中被重写方法的返回值类型

>=        权限修饰符
        子类中的权限修饰符一定要大于或等于父类中的修饰符

        1.必须要发生继承关系
        2.必须保证同名
        3.实在不清楚自己的编写是否产生了重名的问题,我们可以在子类的方法上面编写代码@Override来验证,不报错,则说明是重写方法。
        4.简单的理解,我们可以把重写理解为屏蔽
        5.当然了,我们也可以使用父类的方法,此时就需要使用super.

2.6.3.不能被重写的方法

        1.被private修饰符修饰的方法不能被重写,可以被继承,但是无权访问
        2.被final修饰符修饰的方法不能被重写
        3.被static修饰符修饰的方法不能被重写,但是我们可以在子类中编写一个方法名相同的方法,只要这个方法也是静态的,即可。
        这是因为静态的方法是归属于自己的,只要我们编写的方法是静态的,那么这个静态方法本来就只归属于这个类,编译器就不会把这个同名的类当作被重写的方法了

2.6.4.重写toString

        当我们打印输出一个对象的时候,默认情况下,会打印该对象的toString方法,如果该对象中有toString方法,就直接调用该方法;如果没有就会去父类中寻找,如果还是没有,则会去父类的父类中寻找,直到找到最终的父类,也就是Object,如果Object都没有该方法,则会报错。
        object中有该方法,该方法的作用是打印输出(当前类的全限定名 + @ +哈希code值对应的十六进制数)。

        而我们一般希望我们调用toString方法的时候,我们一般以往可以展示该对象中的所有信息,所以我们可以利用重写来实现。
        创建一个Animal类,在这个类中有一些属性,type、name、weight,添加一个带参构造器,在主方法中创建一个对象,调用toString方法。

public class Animal {
    //字段
    pubilc String type;
    pubilc String name;
    pubilc float weight;
    //空构造
    public Animal () {

    }
    //带参数构造
    public Animal(String type, String name, float weight) {
        this.type = type;
        this.name = name;
        this.weight = weight;
    }
    //主方法
    public static void main(String[] args) {
        //创建实例
        Animal an1 = new Animal("Dog", "哈士奇", 1.25F);
        System.out.println(an1.toString());
    }
}

        使用Object中的toString方法

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
运行结果为com.yjxxt.lesson05rewrite.Animal@776ec8df

         对toString进行重写之后,调用toString方法

public String toString() {
    return  "type:" + type + "---->name:" + name + "---->weight:" + weight;
}
运行结果为type:Dog---->name:哈士奇---->weight:1.25
2.6.5.重写equals方法

==            用来比较是否相同

        基础数据类型,比较数据值是否相等
        引用数据类型,比较的是引用的数据类型的地址值是否相等   

equals方法       用来比较两个对象是否相等的。

    public boolean equals(Object obj) {
        return (this == obj);
    }

        我们可以发现在Object中的equals方法本质上还是按照==的比较来得出结果的。
        但是我们对于引用数据类型,按照人的思维来讲,当应用数据类型中保存的内容是一致的,那么我们就认为这两个引用数据类型的数据是相同的,而在计算机中,只要内存地址不一样,那就是不同的数据。
        我们也可以通过方法重写来解决这个问题。

    public boolean equals(Object obj) {
        //判断两者的内存地址,当内存地址相同时,必然为相同的,直接返回true,不需要再判断对象中保存的内容了
        if (this == obj) {
            return true;
        }
        //判断传入的类型和调用该方法的类型是否一致,这里以Animal类举例
        if (obj instanceof Animal) {
            //当类型一致的时候,本质上我们传入的值是多态写法,类型为Object,此时强转是完全可以保证数据不发生丢失的
            Animal aniObj = (Animal) obj;
            //对里面的内容进行比较
            if ((this.type).equals(aniObj.type) && (this.name).equals(aniObj.name) && this.weight == aniObj.weight) {
                return true;
            }
        }
        return false;
    }
2.6.6.注意

        定义JavaBean的时候需要从写toString和equals

2.7.疑惑 2.7.1.为什么父类要写这样无用代码

        父类中实际上是我们编写的子类的共同特征的总结,也就是说,满足这个特征的才可以被称为是该父类的子类,就像哈士奇不可能是猫类的子类一样,即使都有毛发,都会叫。
        也可以说,父类就是对子类的一些约束。可能有一些无用的方法,但是只有有这个方法的,才是该类的子类

三、多态

        一种事物的多种形态|多种表现形式【行为多态】

3.1.为什么要使用多态?

        当父类想要使用子类中重写的方法的时候,就可以使用多态来进行。

3.2.多态的表现形式

        父类的引用指向子类类型的对象

Animal an = new Dog();

        在此例中,new关键字创建了一个Dog类的实例,将其赋值给了一个Animal类的变量。

        在正常的赋值中,都是定义什么数据类型的变量,则给这个变量赋值就赋什么类型的值
        多态则实现了将某个数据类型的值赋值给其他数据类型的变量,这个其他数据类型必须是值得父类类型

3.3.多态的前提

        前面已经说过了多态的表现形式是有父类有子类的,所以它首先的是继承的关系。
        两者之间是继承之间的关系

3.4.多态的调用 3.4.1.调用实例成员

        直接通过对象名.调用即可
        调用父类中存在的成员,对子类新增内容不可见

3.4.2.调用方法

重写的方法

        调用子类重写的方法
        使用的是子类重写后的那个方法

非重写的方法

        直接使用父类的那个方法

3.5.多态的注意事项

        1.当多态不配合方法重写使用的时候就没有了意义

四、包和导包 4.1.包

        所谓的包我们可以理解成文件夹的意思,我们可以通过包来对类进行一个分类,甚至是使用同名类。

4.1.1.包的使用

语法:

        package  包名.类名(这里的包名是全限定名)

package com.yjxxt.block02;

        这行代码的意思是,我们所创建的这个java类文件所在的包路径。

4.1.2.包的作用

        1.指定当前类在哪个具体的包下,包是路径的意思
        2.package这个声明的代码必须要在当前类文件的首行
        3.包的主要作用是为了区分类。使得当我们要使用某一个类的时候,可以确切的找到这个类
        4.包还可以帮助我们管理众多的资源

4.2.导包

        当我们要使用到其他包下的类中的一些内容(方法等)的时候,就需要用到导包。

4.2.1.什么时候不需要导包?

        1.使用同一个包下的其它类中的内容
        2.java.lang下的内容

4.2.2.导包的使用

语法:

        import 包名.类名(全限定名)

编写位置:

        package语句的下方,类外

package com.yjxxt.block02;
import java.util.Scanner;
public class Class002_BlockTest {
}
4.2.3.导包方式

方式一:使用包名.类名的方式(全限定名)

语法:

        包名.类名

java.util.Scanner sc = new java.util.Scanner();

         在此例中,java.util是包名Scanner是类名

目的:

        为了使用其他包下的类中的内容。

优点:

        可以使用其他包下的类中想要使用的内容,即使内容的命名是相同的,也是可以做到区分的。
        比如,类A和类B中都有add方法,当我们想要在类C中使用这两个add方法的时候,我们就可以通过这个方式来做到使用时的区分。

缺点:

        只能在当前位置生效一次,是当前位置,不在这个位置,哪怕是同一行语句,也不会生效。
        这样的话,我们如果在这个类中需要多次使用某个方法,就需要在每个用到的地方都通过包名.类名的方式来使用,这样无疑是很麻烦的。
        可不可以尽量简化一下呢?

解决方式:

        使用方式二导包

方式二:使用import关键字来进行导包

语法:

        import 包名.类名(全限定名)

//导入
import java.util.Scanner;
//使用
Scanner sc = new Scanner();

目的:

        方式二让我们在多次使用其他类中某一方法的时候比使用方式一要简单很多

优点:

        1.我们使用方式二的方式引入之后,我们就可以直接通过类名来创建对象了;或是通过类名.方法名来使用我们想要使用的一些方法
        2.方式二对于一些不同类的同名变量是无能为力的,当需要使用到不同类的同名变量的时候,我们需要联合方式一一起使用

缺点一:

        当我们需要使用同一个类中的多个内容时,就需要多次导入同一个类种的不同的内容,这些代码无疑是有很大一部分重复的,我们有没有一种方式,可以让我们只需要导入一次,就可以使用多个不同内容呢?

解决方式:

        使用方式三来导包

缺点二:

        使用方式二还有一个不明显的缺点,就是我们使用方式二的时候,在使用的时候还是需要通过类名.来调用我们想要使用的内容

解决方式:

        使用方式四

方式三:模糊匹配

语法:

        import 包名.类名.*

目的:

        使用模糊匹配可以将想要导入的类下的所有内容都一并导入
        这种方式代表着我们需要把该类中所有的东西都导入;这样我们就可以使用不同的方法而不需要对每个方法都编写导入的代码。

import java.lang.Math.*;

        这就意味着,我们把java.lang包下的Math类中的所有静态内容都导入。

缺点:
        但是这种全部导入的方式,会让我们程序的编译速度减少,运行速度则不受影响。

方式四:静态导入[jdk1.5之后支持]

语法:

        import static 包名.类名.要使用的静态变量名或方法名

目的:

        当我们需要多次使用某个包下某个类种的一个静态的变量或是方法的时候,我们就可以通过这样方式导入,当我们想要调用这个方法或是变量的时候,我们只需要通过变量名就可以使用

import static java.lang.Math.PI;
4.2.4.总结

        1.用String、Math、System的时候,无需导包,java.lang包下的所有类都可以直接使用
        2.全限定名就是包名.类名
        3.当我们在一个类中使用某个类。这个类在不同的包下有定义的时候,我们可以通过导入的方式来明确我们要使用的类;
        4.当我们 在一个类中要同时使用不同包下的同名类的时候,只有一个类是通过导入来明确的,其他的类则需要通过 包名.类名的方式来引入
        5.jdk1.5之后提供了静态导入:import static 包名.类名.属性/方法

五、权限修饰符

        成员修饰符,只能修饰成员,不能修饰非成员内容

5.1.权限修饰符的分类 5.1.1.pubilc

        公共的
        所有的人都可以访问到,是一个公共的,权限最开放

5.1.2.protected

        受保护的
        只能在包内或是通过继承的关系来进行访问

5.1.3.default

        默认的
        只能在包内访问

5.1.4.private

        私有的
        只能在当前类中访问

5.1.5.注意事项

        在这四个修饰符中,能修饰种类的只有pubilc和default两种。

5.1.6.修饰符总结表格

5.2.权限修饰符的使用 5.2.1.父类自己创建对象

本类

        在本类中,通过父类实例化创建的对象,所有修饰符修饰的内容都允许访问

同包不同类

        在同包下,通过父类实例化创建的对象,只有private修饰符修饰的内容不允许访问

不同包

        在不同包下,通过父类实例化的对象,只有pubilc修饰的内容允许访问

5.2.2.子类创建对象

同包

        在同包下,通过子类实例化的对象,只有private修饰符修饰的内容不允许访问

不同包

        在不同包下,通过子类实例化的对象,只有pubilc和protected修饰的内容允许访问

六、final修饰符

        final:最终的、成员修饰符
        从final修饰符的含义我们就可以看出,被final修饰符修饰的内容是不可更改的。
注意:
        这里的不可更改的意思是,当一个final修饰的内容在被初始化后,不可更改。
        当一个内容被初始化后,他在内存中的指向就被固定了下来,此时更改这块内存中保存的值是可以的。就像我们最终指向的是一个对象,改变对象中的值,并不会让这个对象本身所在的内存地址发生改变,对于final修饰的变量而言,它所指向的内存地址是没有变化的。

6.1.final的使用

        final是一个修饰符,他被用来修饰变量或方法

6.1.1.常量(变量)语法

声明

        final 数据类型 常量名;

final int I;

初始化

        变量名 = 字面值;

I = 10;
6.1.2.方法语法

        final {void|数据类型} 方法名(参数列表) {}

final void add() {

}
6.2.final的注意事项

        1.final是可以用来修饰变量的,被final修饰的变量是常量。
        这里的变量包括局部变量、类变量、实例变量。
        2.被final修饰的方法不能被重写
        3.被final修饰的类不能被继承【太监类】
       

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5438487.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存