Java面向对象编程

Java面向对象编程,第1张

文章目录
  • 面向对象编程
    • 关于访问权限
    • 封装
      • default 包访问权限
      • import关键字
        • 静态导入
    • 常见的系统包
    • 继承
      • protected继承权限
    • this和super
      • this关键字
      • super关键字
      • this和super的区别
    • final关键字
    • 组合关系
    • 多态
      • 向上转型
      • 方法重写
      • 方法重载和方法重写的区别
      • 向下转型
    • 抽象类
    • 接口

面向对象编程 关于访问权限

private(私有的,当前类中可见)

default(包访问权限,当前包中可见,不包含子包,同级目录下可见)

protected(继承权限,不同包有继承关系的类之间可见)

public(共有的,当前项目可见)

这四个访问权限的可见范围由小到大

private < default < protected < public

封装

封装:使用private关键字将属性/方法进行封装,这个属性/方法就对外部隐藏了,只在当前类的内部可见。

封装增加了保护性和易用性

保护性:自然是外部无法直接访问,只能通过提供的方法来访问,意思就是规则由我来定,自然增加了保护性

易用性:当你通过程序提供的方法来 *** 作属性,你并不需要知道具体是如何运行的,你只需要知道你 *** 作的结果是怎样的(例如:电脑一键启动,你只需要知道电脑按开机键就能启动,至于具体的cpu怎么运行,显卡又怎么运行你当然是不知道的)

private访问权限就是在当前类的内部可见

我们要了解包访问权限,首先要知道什么是包

Java中的‘包’对应 *** 作系统就是文件夹,使用package关键字声明一个包,若存在多个文件夹的嵌套,使用‘.'分隔

default 包访问权限

没有任何修饰符的权限就是包访问权限,意味着当前包中的所有类都可以访问这个成员,但也是仅限于当前包,连字包也不可见

若你在package1中定义了一个包访问权限的Test类,这个package1还有一个字包package2,这时package2是不能直接使用Test的,这也带来了个好处,那就是不同包中是可以重名的

import关键字

而若此时我们想要在一个包中使用另外一个包中的类

这时就需要用到import这个关键字,来导入包中的某个具体类

import 包名.类名;

如我们导入java中util包中的Date类

import java.util.Date;

需要注意的是import导入的只是某个包中的某个具体的类,并不是导入这个包

而若我们此时需要用到util包中的多个类,这时一句一句写的话就有些麻烦,可以使用

import 包名.*;

还是需要注意,这时的导入依然是导入包中的具体某个类,并不是导入这个包,这点非常要注意不要产生误解,只是这样导入用到这个包中的类的时候,会按需加载,你使用该包下的哪个类就会自动加载哪个类

但是这样使用会有一个问题,那就是重名问题

若两个包内都有同样的名字的类,这时就会让系统无法识别到底是使用哪个包中的类

package Test;

import java.util.*;
import java.sql.*;

public class Test {
    public static void main(String[] args) {
        Date date = new Date();
    }
}

util和sql这两个包中都有Date这个类,这时就会出现以下报错

这就是使用import需要注意的两个问题

1.import导入的始终是某个具体的类,并不会导入某个包

2.import 包名.*;需要注意重名问题

关于import 包名.*;的重名问题的解决方法有两个

1.直接使用类的全名称

package Test;

import java.util.*;
import java.sql.*;

public class Test {
    public static void main(String[] args) {
        //这时就是明确表示使用的是util包下的Date类
        java.util.Date date = new java.util.Date();
    }
}

2.明确指定导入的是哪个包下的类

package Test;
//明确指明了此时Date类就是util包中的Date类
import java.util.Date;
import java.sql.*;

public class Test {
    public static void main(String[] args) {
        Date date = new Date();
    }
}
静态导入

静态导入就是使用import和static关键字导入某个包中的静态方法和属性

import static java.lang.System.*;

这就表示导入了System这个包中的所有静态方法和属性

这样我们在使用的时候就相当于,这个方法是你自己写的,存在于你这个类中

//导入System中的所有静态方法和属性
import static java.lang.System.*;

public class Test {
    public static void main(String[] args) {
        //使用之前经常使用的打印方法
        System.out.println(123);
        //把包名省略一样可以使用
        out.println(123);
    }
}

//打印结果
123
123

虽然是可以这样,但并不推荐使用,本来使用类全称.静态方法/静态属性 来访问静态域已经很简单了,省不省略这一步也无伤大雅

常见的系统包
  • java.lang :JDK的基础类,System,String,Object

  • java.lang.reflect:反射开发包

  • java.util :工具包(集合类都在这个包下,Arrays,LinkedList,HashMap)

  • java.io:I/O 开发包 ,文件读取和写入

  • java.net:网络编程开发包,Socket

  • java.sql:数据库开发用到的包

继承

protected继承权限

我们要了解继承权限,首先要清楚什么是继承

定义了三个类,分别是Person,Chinese和American

package Person;

public class Person {
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}
package Person;

public class Chinese {
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}
package Person;

public class American {
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}

这三个类确实都有,名字,年龄,以及吃饭和睡觉这两个行为,这是肯定的,但我们发现,这三个类的代码时完全一样的,这就造成了代码冗余

而且这时,Person和Chinese以及American是满足is a关系的

Chinese is a Person

American is a Person

这时就可以使用关键字extends来继承

package Person;

public class Person {
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}
package Person;

public class Chinese extends Person {
}
package Person;

public class American extends Person {
}
package Person;

public class Test {
    public static void main(String[] args) {
        Chinese chinese = new Chinese();
        chinese.name = "中国人";
        chinese.age = 18;
        System.out.println(chinese.name + " " + chinese.age);
        chinese.eat();
        chinese.sleep();

        American american = new American();
        american.name = "美国人";
        american.age = 18;
        System.out.println(american.name + " " + american.age);
        american.eat();
        american.sleep();
    }
}

这时Chinese和American下一句代码都没有,但却是能正常使用

继承可以避免代码冗余,但不能为了省略代码就简单的使用继承

要想使用继承必须满足 is a 关系,如Chinese is a Person,American is a Person

若一个类和另一个类满足 类 is a 另一个类 一定存在继承关系

继承的规则:

1.要能使用继承前提必须满足类之间的 is a 关系

2.在Java中一个子类只能使用extends继承一个父类

在Java中不允许多重继承,但允许多层继承

就如哈士奇 is a Dog,哈士奇 is a Animal ,虽然都存在继承关系,但不能

public calss erha extends Dog,Animal{
}

这在Java中是不允许的,但是C++中是有多重继承的

但Java可以多层继承

public calss erha extends Dog{
}
public calss Dog extends Animal{
}


3.子类会继承父类的所有属性和方法(显示继承和隐式继承)

显示继承:所有public属性和方法可以直接使用

显示继承不用多说,可以直接访问到继承的方法或属性

隐式继承:private属性和方法,子类其实也继承了这个属性和方法,但是无法直接使用只能使用父类提供的方法来 *** 作

就如我们将Person中的age属性改为private权限

public class Person {
    public String name;
    private int age;
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}


就会出现以上报错,虽然他继承了,但是没有权限访问,那么如何证明他确实继承了呢?

public class Person {
    public String name;
    private int age;
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}

我们可以在Person这个类中,设置setter和getter方法,由我们提供方法来让age可以被 *** 作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c9U0kFzE-1651829228597)(E:\桌面\extends3.jpg)]

这时就能说明他确实是继承了,但是是隐式继承,并不能直接 *** 作,只能通过提供的方法来 *** 作,这就是隐式继承

了解完了继承就可以来看看 protected继承权限

protected继承权限

protected继承权限的可见范围是,当前包中不同类可见以及不同包中有继承关系的类的内部可见

将Person类中的age改为继承权限

public class Person {
    public String name;
    protected int age;
    public void eat(){
        System.out.println(this.name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "正在睡觉");
    }
}

这时在同包中的不同类(无继承关系)中是可见的,这个同包并不包括这个包下的子包


知道了同包中不同类可见,那还有不同包中有继承关系的类中也可见


Japanese这个类是在Japanese这个包中的,并不在Person这个包中,但却可以直接使用age这个属性且没有报错,这就说明protected修饰的age属性在不同包有继承关系的类中的可见的

this和super this关键字

this关键字:表示当前对象的引用

this修饰属性,表示直接从当前类中找同名属性

当有继承关系时,默认先当前类中寻找同名属性,若当前类中没有则会在父类中去寻找

,若任不存在继续向上寻找

this修饰方法也一样,先是从当前类中寻找,之后向上寻找

super关键字

super关键字:表示从直接父类中开始寻找(super关键字先直接父类寻找,若不存在继续向上寻找)

修饰属性,表示直接从父类中去寻找同名属性,找到之后就不再继续向上寻找

public class Person {
    private String name;
}
public class Chinese extends Person {
    public String name;
    public void fun(String name){
    	super.name = name;
    }
}

尽管父类的name属性被private修饰无法直接访问,但依然是找到的是父类同名的name方法,只是没有权利访问这个name属性,且既然找到了也不会再继续在Person(假设Person还有父类)的父类上再寻找,只有父类不存在才会继续向上寻找

修饰方法,表示直接从父类中去寻找方法

而这涉及到一个知识,就是有继承关系的类,要想产生子类的对象,首先要产生父类对象,就比如没有你的父亲,哪来的你

public class Person {
    public Person(){
        System.out.println("1.Person的无参构造");
    }
}
public class Chinese extends Person {
    public Chinese(){
        System.out.println("2.Chinese的无参构造");
    }
}
class Test {
    public static void main(String[] args) {
        Chinese chinese = new Chinese();
    }
}


可以看到,是先打印了Person的无参构造,说明是先产生了Person对象再产生Chinese

public class Person {
    public Person(){
        System.out.println("1.Person的无参构造");
    }
    {
        System.out.println("2.Person的构造块");
    }
    static{
        System.out.println("3.Person的静态块");
    }
}
public class Chinese extends Person {
    public Chinese(){
        System.out.println("4.Chinese的无参构造");
    }
    {
        System.out.println("5.Chinese的构造块");
    }
    static{
        System.out.println("6.Chinese的静态块");
    }
}
class Test {
    public static void main(String[] args) {
        System.out.println("7.主方法开始");
        Chinese chinese = new Chinese();
        System.out.println("8.主方法结束");
    }
}

这个Chinese对象执行的顺序是怎样的?

super修饰构造方法

直接就是super();直接调用父类的无参构造(可选择写或不写),若父类中没有无参构造,则在子类的构造方法的首行必须使用super(参数)来调用,不能够省略

public class Person {
    public String name;
    public Person(String name){
        this.name = name;
        System.out.println("1.Person的无参构造");
    }
}
public class Chinese extends Person {
    public Chinese(){
        super();
        System.out.println("2.Chinese的无参构造");
    }
}

这时是会报错的


还有需要注意的就是在一个构造方法中this和super是不能同时显示使用的

public class Person {
    public String name;
    public Person(){
        this.name = name;
        System.out.println("1.Person的无参构造");
    }
}
public class Chinese extends Person {
    public Chinese(){

    }
    public Chinese(String name){
        super();
        this();
        System.out.println("2.Chinese的无参构造");
    }
}


而不显示使用是没问题的,例如把super();删了

public class Chinese extends Person {
    public Chinese(){
        System.out.println("3.Chinese的无参构造");
    }
    public Chinese(String name){
        this();
        System.out.println("2.Chinese的有参构造");
    }
}


一样是先上升到父类,产生父类对象,再回来调用构造方法,也就是在this();前面默认有一个super();

super修饰普通方法

super修饰普通方法,和修饰属性一样,直接从父类中寻找同名方法

this和super的区别

super关键字不能指代当前父类的引用

this关键字表示当前类的对象的引用

System.out.println(this);
System.out.println(super); //报错
//不能使用super来表示当前父类的引用,但是使用super.方法/属性是可以的
System.out.println(super.name);
System.out.println(super.toString);
final关键字
  • final修饰属性,表示该属性具备了常属性,不能被更改

  • final修饰类,表示这个类无法被继承

  • final修饰方法,表示这个方法无法被覆写

组合关系

类和类之间的关系,除了继承关系还有组合关系

组合关系即使 has a 关系

class School{

​ Student student;

​ Teacher teacher;

}

School has a student,School has a teacher,在School里面有着多种类

多态

多态:一个引用可以表现出多种行为/特性,就称之为多态

向上转型

之前我们创建一个对象都是使用,类名称 类引用 = new 该类对象();

如:Chinese chinese = new Chinese();

而我们还可以写成这样

Person per = new Chinese();

父类名称 父类引用 = new 子类对象(); (这个子类不一定是直接子类,但前提是有继承关系)

例如 Erha extends Dog; Dog extends Animal;

Animal animal = new Erha();

这个称之为向上转型,向上转型的最大意义在于参数的统一化,降低使用者的使用难度

若我们没有向上转型,我们写一个fun方法来接收Animal及其子类作为参数,这时Animal有多少个子类,我们就得重载多少次fun方法

且在使用时,若没有向上转型,我要使用fun方法,就得了解Animal以及其所有子类的对象,我才能知道调用的是谁


只需要知道是否是该类的子类就能判断是否能够使用这个fun方法,且还避免了多次重载代码

只要是Animal的子类都能够使用fun这个方法,有了向上转型之后,就能使用最顶端的父类的引用就可以指代所有的子类对象

且当有个新类也是这个类的子类是非常容易扩展的,例如cat也是Animal

public class Cat extends Animal{}


一样是可以使用这个方法

而多态就是基于向上转型和重写的

我们在Animal、Dog和Erha都定义了一个eat方法

public class Animal {
    public void eat(){
        System.out.println("Animal的eat方法");
    }
}
public class Dog extends Animal {
    public void eat(){
        System.out.println("Dog的eat方法");
    }
}
public class Erha extends Dog{
    public void eat() {
        System.out.println("Erha的eat方法");
    }
}

在fun方法中我们调用eat方法看看怎么样的

public class Test {
    public static void main(String[] args) {
        fun(new Animal());
        fun(new Dog());
        fun(new Erha());
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}

我们可以发现,他用的是各自类的eat的方法,而不是Animal的eat方法

这就是多态,一个引用可以表现出多种行为/特性

而各自类的eat方法称之为方法重写,那方法重写是什么呢

方法重写

方法重写(override):是发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全部相同的方法,这样的一组方法称之为方法重写

就如上面Animal、Dog和Erha的eat方法

方法重写之后具体调用的是谁,我们只需要看new的是哪个对象即可,new的是哪个对象且该对象有重写该方法,调用的就是该对象自己的方法

而若此类中没有重写该方法,则是根据就近匹配原则,去向上找同名方法,先是找直系父类,若直系父类没有重写,再继续向上找

当发生重写时,子类的权限必须 >= 父类权限才可以重写

private < default < protected < public

但又一个权限例外,private权限并不在这之内,方法重写只能是default、protected、public权限

为什么呢?

当你使用private权限修饰方法的时候,只要出了这个类的内部,外部就无法知道有这个方法的存在,既然连它的存在都不知道,那我还怎么去重写这个方法

注意事项

方法重写,不能重写static方法,多态的本质就是因为调用了不同的子类对象,只有这些子类覆写了相应的方法,才能表现出不同的行为,但是static和对象无关,它修饰的方法是属于类的

所以方法重写只发生在普通方法中

方法重载和方法重写的区别
区别重载(overload)重写(override)
1概念方法的名称相同,参数列表不同、与返回值类型无关方法名称、返回值类型、参数列表完全相同,只与权限有关
2范围一个类中存在继承关系的类中
3限制没有权限要求被覆写的方法的权限要大于父类的权限
4static没要求无法重写static方法
向下转型

既然有向上转型,那肯定就有向下转型

父类名称 父类引用 = new 子类对象();

子类和父类是存在is a 关系所以这是天然发生的向上转型

Animal animal = new Dog();

若此时我们Dog中还有个自己独有的play();方法,我们需要调用这个play();方法,此时就不能使用animal.play();来调用了,

public class Animal {
    public void eat(){
        System.out.println("Animal的eat方法");
    }
}
public class Dog extends Animal {
    @Override
    public void eat(){
        System.out.println("Dog的eat方法");
    }
    public void play(){
        System.out.println("Dog的play方法");
    }
}
public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();
        animal.play();
    }
}

这时就会报错,我们并不能这样使用

那么我们想要调用这个play方法就需要向下转型,才能调用

使用 子类名称 子类引用 = (子类名称) 父类引用就可以将animal向下转型为Dog类

使用dog(子类引用)就能访问到Dog自己的方法

public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();
        Dog dog = (Dog)animal;
        dog.play();
    }
}

要想知道animal能调用什么方法,只需要看Animal有什么方法即可,而具体的表现形式,若子类有重写次方法就表现出子类的方法

向下转型还有个条件

要发生向下转型,首先要发生向上转型

Animal animal = new Dog();
Dog dog = (Dog)animal;

这样是可以向下转型的,因为Dog先向上转型为animal了,而

Animal animal = new Animal();
Dog dog = (Dog)animal;

此时这个animal与Dog类毫无关系,所以animal无法向下转型

Animal animal = new Dog();

就如披着Dog皮的Animal,本质上它依然是个Dog

看到这个就得反应是类型转换异常

当发生向下转型时会有风险,类型转换异常,使用instanceof关键字

引用名称 instanceof 类 -> 返回布尔值 ,表示该引用指向的本质是不是该类的对象

检查animal1和animal2是否是Dog这个类的对象,是返回true否则返回false

抽象类

若需要强制要求子类覆写方法,可以使用抽象类

抽象类是概念化的,没办法具体到某个实例,描述这一类对象共同拥有的属性和行为

如形状,说到形状你一下并不能反应出具体的形状,但这个形状和其子类圆形、正方形、三角形都有一些共同的属性和行为

抽象类是普通类的‘超集’,就是普通类有的抽象类都有,只是抽象类比普通类多了一些抽象方法

抽象方法所在的类必须是抽象类,子类若是继承了抽象类,必须覆写所有抽象方法(前提是子类是普通类)

在Java中使用abstract关键字定义抽象类或者抽象方法

1.抽象方法所在的类必须使用abstract声明为抽象类

这个print方法就是一个抽象方法,所以声明Sharp为一个抽象类

抽象方法只有方法声明,没有方法体

若一个方法定义为抽象方法,在抽象类中是没有具体的实现的,是延迟到子类中实现

注意事项

不能说没有方法体的方法就是抽象方法

2.若一个类使用abstract声明为抽象类,那这个类是无法实例化对象的,哪怕该类中没有抽象方法

只能是通过子类向上转型为抽象父类的引用

Sharp sharp = new Sharp();

这样是不行的,直接报错

3.子类继承了抽象类,必须强制覆写抽象类的所有抽象方法(前提是子类是普通类),且任然是只能继承一个父类

public abstract class Sharp {
    public abstract void print();
}
public class Cycle extends Sharp{
    @Override
    public void print() {
        System.out.println("●");
    }
}
public class Triangle extends Sharp{
    @Override
    public void print() {
        System.out.println("▲");
    }
}
public class Square extends Sharp{
}

若没有覆写抽象方法则会报错

4.抽象类是普通类的‘超集’,只是比普通类多了一些抽象方法,虽然抽象类无法实例化对象,但也是可以存在构造方法,子类在实例化是仍然遵循继承的原则,先调用父类(抽象类)的构造方法,然后调用子类的构造方法产生子类对象

5.虽然抽象类无法实例化对象,但子类也是需要满足is a原则,子类和抽象父类之间仍然是满足“继承树”的关系

接口

接口的使用一般表示两种场景

1.接口表示具备某种能力/行为,子类实现接口时不是is a 关系,而是具备这种能力

例如:跑步–> Person、Dog、Cat等各种类都具备跑步这个行为,所以都能实现跑步这个接口

2.接口表示一种规范或者标准

如:“USB接口”;“5G标准”

在接口中只有全局常量和抽象方法,其他东西统统没有

在Java中使用interface来声明接口,子类则使用implements实现接口

示例:USB接口

首先在new的时候要选择interface

对于USB接口来说他有两个核心方法 那就是插入方法和工作方法

public interface USB {
    public abstract void plugIn();
    public abstract void workOn();
}

这是就定义了一个USB接口

由于接口中只有抽象方法和全局常量,他是延迟到子类中实现的,USB的子类就有鼠标,键盘等

在子类中使用使用implements实现接口,必须覆写所有的抽象方法(前提是子类是普通类)

public class Keyboard implements USB{
    @Override
    public void plugIn() {
        System.out.println("插入键盘,安装键盘驱动");
    }

    @Override
    public void workOn() {
        System.out.println("键盘正常工作");
    }
}
public class Mouse implements USB{
    @Override
    public void plugIn() {
        System.out.println("插入鼠标,安装鼠标驱动");
    }

    @Override
    public void workOn() {
        System.out.println("鼠标正常工作");
    }
}

这就是我们实现的两个子类

我们来模拟一下电脑的USB接口

public class Computer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Mouse mouse = new Mouse();
        computer.fun(mouse);
        Keyboard keyboard = new Keyboard();
        computer.fun(keyboard);
    }
    public void fun(USB usb){
        usb.plugIn();
        usb.workOn();
    }
}

注意事项

接口是允许多实现的,就如一个子类Person他既会跑又会游泳

public interface IRun {
    public abstract void run();
}
public interface ISwim {
    public abstract void swim();
}
public calss Person implements IRun,ISwim{
    @Override
    public void run(){
        
    }
     @Override
    public void swim(){
        
    }
}

接口表示一种能力,一个类当然可以具备多种能力,就如一个人有各种各样的技能

还有需要注意的是,因为接口只有抽象方法和全局常量,所以在接口中

public abstract 和 static final 是可以省略的

只需要保留最核心的方法返回值,方法参数列表,方法名称即可

public interface ISwim {
    void swim();
}

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

原文地址: http://outofmemory.cn/langs/871652.html

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

发表评论

登录后才能评论

评论列表(0条)

保存