面向对象编程

访问修饰符

权限从小到大依次为

private私有,当前类的内部可见
default啥也不写就是包权限,当前包的内部可见,不包含子包,同级目录下可见
protected继承,不同包的有继承关系的类之间可见
public当前项目可见
  • Java中的就是操作系统的文件夹,声明一个包使用package关键字

  • 若存在多个文件夹的嵌套,使用"."分隔符,创建多个文件夹

  • 类的全名称:包名.类名

  • 导入某个包中的某个类 import导入类只可导入相关包中的某个具体的类

  • import java.util.*//此时将整个util包下的所有类按需加载

当程序用到了两个相同名称的类

  1. 使用类的全名称
java.util.Date date = new java.util.Date();
java.sql.Date date1 = new java.sql.Date();
  1. import明确指定导入的是哪个包下的哪个类

import java.util.Date;

静态导入

import static可以导入包中的静态方法和静态属性

常见的系统包

java.langJDK的基础类,System,String,Object都在这个包下
java.lang.reflect反射开发包
java.util工具包(集合类都在这个包下,Arrays,LinkedList,HashMap)
java.ioI/O开发包,文件读取和写入
java.net网路编程开发包,Socket
java.sql数据库开发

封装

封装:使用private将属性进行封装(这个属性只在当前类的内部可见,对外部隐藏)

方法重载

什么是方法重载?

在同一个类中,定义了若干个方法名称相同,参数列表不同,与返回值无关的一组方法。这样的一组方法称为方法重载。

继承

package Animal;
public class Animal{
    public String name;
    public void eat(String food){
        System.out.prinlnt(this.name + "正在吃" + food);
    }
}
package Dog;
public class Dog{
    public String name;
    public void eat(String food){
        System.out.println(this.name + "正在吃" + food);
    }
}
package Cat;
public class Cat{
    public String name;
    public void eat(String food){
        System.out.println(this.name + "正在吃" + food);
    }
}

这三个类的代码完全一样

按道理说所有Animal的类都应具备name属性以及eat

Dog is an Animal

Cat is an Animal

继承(extends)

当一个类继承了另一个类,另一个类中所有的属性和方法,子类就天然具备了

Java中使用extends表示类的继承

Dog extends Animal

继承的规则

  • 必须满足is a关系
  • 一个字累只能使用extends继承一个父类(单继承)
  • 子类会继承父类的所有属性和方法,显式继承(public属性和方法可以直接使用,隐式继承(private属性和方法),子类其实也继承了这个属性和方法,但是无法直接使用

隐式继承,子类需要通过父类提供的方法来操作getter and setter

问答环节

问:静态的成员和方法可以直接继承吗?

答:静态的属性和方法是归于某个类所有

当一个类继承了另一个类,肯定所有的静态属性喝方法就继承

能否使用,看权限是否为public

关于protected访问权限

protected作用域不同包中的子类可见

public class Test{
    public static void main(String[] args){
        Animal animal = new Animal();
        //System.out.println(animal.name);//Test不是Animal的子类且不在一个包中,因此name对于Test类来说不可见
    }
}
public class Test{
    public static void main(String[] args){
        Person per = new Person();
        //per.name;//这行代码发生在Test这个类中,protected只在当前类和子类的内部可见,所以出错
}
}

同包下的没有关系的类之间以及不同包的有继承关系的类之间可见


this关键字

this:表示当前对象的引用

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

修饰方法,表示当前对象的引用

要产生一个子类对象,默认首先产生父类对象

class Dog extends Animal{}

Dog dog = new Dog();//产生一个子类Dog对象

  • 当有继承关系时

this关键字默认先在当前类中寻找同名属性,若没找到,继续向上寻找父类中是否有同名属性

直接使用name。编译器默认都是this.name

super关键字

修饰属性,表示直接从父类中去寻找同名属性

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

子类构造方法默认带有super();表示调用父类无参构造,且一定在首行(首条语句)
在这里插入图片描述

super关键字

super修饰属性 表示从父类中寻找同名属性,若不存在则再向上寻找

this直接从当前类中寻找同名属性,若不存在则再向上搜索。

super修饰构造方法

  • super(父类构造方法的参数)
  • super();//直接父类的无参公祖奥,可写可不写

若父类中不存在无参构造,则子类构造方法的首行必须使用super(有参构造)

在一个构造方法中无法显式使用this()和super()同时出现。


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

  • super不能指代当前父类的对象引用
System.out.println(this);
//System.out.println(super);//java:需要'.'

前方高能!!!

package animal;

public class B {
    public B() {
        System.out.println("1.B的构造方法---------------");
    }
    {
        System.out.println("2.B的构造块-----------------");
    }
    static {
        System.out.println("3.B的静态块-----------------");
    }
}

package animal;

public class D extends B{
    public D() {
        System.out.println("4.D的构造方法--------------");
    }
    {
        System.out.println("5.D的构造块----------------");
    }
    static {
        System.out.println("6.D的静态块----------------");
    }
    public static void main(String[] args) {
        System.out.println("7.main开始。。。。");
        new D();
        new D();
        System.out.println("8.main结束。。。。");
    }
}

问输出结果顺序是什么?

在这里插入图片描述

由于main存在于D子类中,若JVM要调用main,首先要加载主类,一加载主类,执行主类的静态块,D继承了B,先加载父类再加载子类

  • 3(父类的静态块)- > 6(子类的静态块)类加载结束后,进入主方法
  • 产生子类对象,先要产生父类对象,先调用构造块{},下来才是构造方法
  • 先调用父类的构造块2,调用父类构造方法1=》父类对象产生完毕,子类构造块5 子类构造方法4=》 子类对象产生完毕

final关键字

在这里插入图片描述

final int num = 10;//num值不可修改
final class Person{}  //Person无法被继承

多态

多态:一个引用可以表现出多种行为/特征 = > 多态性

向上转型: 最大的意义在于参数统一化,降低使用者的使用难度!

Animal animal = new Dog();

父类名称 父类引用 = new 子类对象();//不一定是直接子类,也可以是孙类。。。

有了向上转型后,最顶尖的父类引用就可以指代所有的子类对象。

package Polymorphism;

public class Animal {//父类
    public void eat() {
        System.out.println("Animal的eat");
    }

    public void play() {
        System.out.println("Animal的play");
    }
}

package Polymorphism;

public class Duck extends Animal {//子类具有父类的属性
    public void eat() {
        System.out.println("Duck类的eat方法");
    }

    public void play() {
        System.out.println("Duck类的play方法");
    }
}

package Polymorphism;

public class Test {//测试输出
    public static void main(String[] args) {
        fun(new Animal());
        fun(new Bird());
        fun(new Duck());
        fun(new Dog());
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}

animal.eat();fun中animal局部变量的引用调用eat方法时,当传入不同的对象时,表现出来了不同的eat方法行为 => 多态性

同一个引用(变量名称),同一个方法名称根据对象的不同表现出来了不同的行为 =》多态

方法重写(override)

  • 方法重载(overload):发生在同一个类中,定义了若干个方法名称相同,参数列表不同的一组方法。
  • 方法重写(override):发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全都相同的方法,这样的一组方法称之为方法重写。

到底调用的是谁的方法呢?

看new的是谁,只要new的这个对象的类中覆写了同名方法,则调用的一定是覆写后的方法

问:若子类没有重写这个方法,调用的是?

就近匹配原则,碰到最接近的调用,子类没有从父类找


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

private<default<protected<public

public class Animal{
    proteted void eat(){
        System.out.println("Animal类的eat方法");
    }
}

package Dog;
public class Dog extends Animal{
    public void eat(){//public>protected可行
        System.out.println("Dog类的eat方法");
    }
    
    protected void eat(){//protected = protected可行
		System.out.println("Dog类的eat方法"))
    }
    
    //void eat(){//default<protected 不可行
	//	System.out.println("Dog类的eat方法"))
    //}
}


问:父类使用private子类使用public可以吗

private权限不包含在内

@Override注解

Java中有一个注解@Override使用这个注解写在重写方法之前,帮你校验你的方法重写是否符合规则。快捷键:alt+insert或者在extends后面直接按alt+Enter自动填补

能否重写static方法?

多态的本质就是因为调用了不同的子类“对象”,这些子类对象所属的类覆写相应的方法

才能表现出不同的行为 而static与对象无关!!!

不可以重写static方法

No区别重载(overload)覆写(override)
1概念方法名称相同,参数类型及个数不同方法名称、返回值类型、参数的类型及个数完全相同
2范围一个类继承关系
3限制没有权限要求被覆写的方法不能拥有比父类更严格的访问控制权限(不能包含private)
4static无要求不能重写static方法

向上转型发生的时机

1.引用赋值

Animal animal1 = new Bird();//重写
Animal animal2 = new Duck();//重写
Animal animal3 = new Dog();//重写
Animal animal4 = new Cat();//重写

2.方法传参 - 使用最多的

fun(animal1);
fun(animal2);
fun(animal3);
fun(animal4);
public static void fun(Animal animal){
    animal.eat();
}

3.方法返回值

public static Animal test(){
   	Bird bird = new Bird();
    return bird;
}

来做个小题吧

问:输出结果为?

public class B {
    public B() {
        fun();
    }

    public void fun() {
        System.out.println("B.fun()");
    }
}
public class D extends B {
    private int num = 10;

    public void fun() {
        System.out.println("D.fun,num = " + num);
    }

    public static void main(String[] args) {
        D d = new D();
    }
}

首先调用了个D,调用D的无参构造,这时存在继承,优先调用B的构造方法先产生父类对象fun()方法,fun()是通过D() new出来的,fun()方法被D类覆写,所以fun()方法调用的是子类覆写后的fun()方法,当调用fun()方法时,这时候还没执行D的构造方法呢,子类对象还没初始化完成呢,D的所有属性都是默认值

D.fun(),num = 0;

public D(){
    super();//此时还在super()中
    private int num = 10;
}

问:父类没有子类扩展方法会怎样?

public void play(){
    System.out.println("Dog类独有的play方法");
}

public static void main(String[] args){
    Animal animal = new Dog();
    animal.eat();
    //animal.play();//父类Animal中没有play方法,不可使用
}

父类Animal中没有play方法,不可使用

向上转型

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

天然发生的向上转型

使用父类引用调用普通方法时,若子类重写了该方法,则调用该对象所在子类覆写后的方法。

能通过"."访问的方法 类名称说了算

能访问的这些方法必须都在类中定义过,编译器会先在类中查找是否包含指定方法

至于这个方法到底表现出来是哪个类的样子,实例所在的方法说了算。

Animal animal = new Dog();
animal.方法名称()

这个方法能不能调用,看Animal,这个引用还是父类引用

animal.eat() =>能调用了,到底是啥样子的eat,看new的实例是通过哪个子类new的,该子类是否重写了eat方法

总结:到底能.哪些方法前面说了算,到底.之后这方法长啥样,后面new的说了算

向下转型

Animal animal = new Dog();
animal.play();

animal这个引用是披着狗皮的动物,本质上是dog,披了个Animal的外衣,此时只能调用Animal中定义的方法,

想调用子类拓展的方法该咋办?

脱掉这层外衣,还原为子类引用 = > 向下转型

public static void main(String[] args){
    Animal animal = new Dog();
    animal.eat();
    //play在Animal中不存在
    //animal.play();报错,不可用
    Dog dog = (Dog) animal;//强转  向下转型
    //Animal animal1 = dog;//还可以再 向上转型 变回去
    dog.play();//只是换了个名称,对象还是那个对象(new了几个,就还是几个)
}

子类名称 子类引用 = (子类名称)父类引用

Dog dog = (Dog) animal;//脱掉animal对应的对象的外衣还原为具体的子类引用

总结

  • 要发生向下转型,首先要发生向上转型
  • 重写:返回值完全相同或者至少是向上转型类的返回值
  • 毫无关系的两种类型不能作为方法重写的返回值(比如父类int返回值,子类boolean)

问:可以把猫变成狗吗?

Animal animal = new Animal();
//Dog dog = (Dog) animal;//不可以 毫无关系的两个类无法强转
Animal animal = new Animal();//本身就是Animal对象,和Dog毫无关系
Animal animal1 = new Dog();//披着狗皮的Animal,本质上还是一个Dog类的对象

问:啥时候需要用到向下转型?

啥时候发生向上转型,方法接收一个类和当前类的子类,参数指定为相应的父类引用,发生的就是向上转型。

只有某个特殊情况下,需要使用子类拓展的方法,才需要将原本向上转型的引用向下转型还原为子类引用。

做个小题

package interface_test;

class Person {
    public void fun() {
        this.test();//Person类中test方法可见,被Student覆写
    }

    Person test() {
        System.out.println("1.Person的test方法");
        return new Person();
    }
}

class Student extends Person {//Student is a Person反过来不可以
    public Student test() {//此时子类使用Student作为返回值,父类Person可以
        System.out.println("2.Student的test方法");
        return new Student();//向上转型
    }
}

public class Test {
    public static void main(String[] args) {
        new Student().fun();
    }
}
//2.Student的test方法
  • 如果改成private Person test(),则输出1.Person的test方法

test方法是private方法,子类根本不知道其存在,也就无法覆写,对子子类来说,这个test方法就是个普通方法,不存在覆写

package a;
class Base{
    protected String name = "猴哥";
}
package b;
class SubType extends Base{
    public void fun(){
        System.out.println(name);//ok
        Base base = new Base();
        System.out.println(base.name);//t or f此时虽然在子类中,使用的是Base的引用直接访问name属性,还是不行的
    }
}

抽象方法(abstract)

向上转型带来最大的好处在于参数统一化,使用一个共同的父类引用,可以接收所有的子类实例

现在有这样的需求:描述形状的类Sharp,三角形,正方形,圆形

要求创建一个方法可以接收Sharp以及所有子类的对象,调用print() =>向上转型用在方法参数

public class Test{
    public static void main(String[] args){
        fun(new Cycle());
        fun(new Square());
        fun(new Triangle());
    }
    public static void fun(Sharp sharp){
        sharp.print();
    }
}

这三个子类都是Sharp的子类且都覆写了print()

此时多态非常依赖子类覆写方法

普通父类没法强制要求子类覆写方法

若要强制要求子类覆写方法,用到抽象类

  • 抽象类是普通类的“超集”,只是比普通类多了一些抽象方法而已
  • 抽象方法所在的类必须是抽象类,普通子类若继承了抽象类,必须覆写所有抽象方法

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

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

抽象方法

抽象方法:指的是使用abstract关键字声明,只有函数声明,没有函数实现{}的方法

ps:没有方法体的方法不一定是抽象方法,比如本地方法没有方法体{}

public abstract class Sharp{
    public abstract void print();
}
  • 一个类若存在抽象方法,必须使用abstract抽象类

  • 抽象类中没有具体实现,在子类实现

  • 若一个类使用abstract声明为抽象类,不管有没有抽象方法,这个类本身就是一个抽象的概念。只能通过子类向上转型变为抽象父类引用。

  • 在IDEA中,类图标带有两个小杠的是抽象类;如果是普通类,则是一个C(class)

Sharp sharp = new Sharp();//error
Person per = new Person();//error
Person per = new China();//ok
  • 普通子类继承了抽象类,就必须强制子类覆写抽象类中的所有抽象方法,也满足单继承局限,一个子类只能extends一个抽象类。
abstract class A {
    abstract void printA();
}

//B是抽象类,可以选择性的覆写父类的抽象方法
abstract class B extends A {
    abstract void printB();
}

//C是普通类,必须覆写B中的所有抽象方法(包括继承来的抽象方法)
public class C extends B {
    @Override
    void printB() {}

    @Override
    void printA() {}
}
  • 抽象类是普通类的超集(普通类有的内容,抽象类都有),只是比普通类多了一些抽象方法而已,抽象类虽然没法直接实例化对象,但是也可以存在构造方法,子类在实例化时,仍遵从继承的规则,先调用父类(抽象类)的构造方法,再调用子类构造方法

  • 抽象类只是普通类的超集,只是比普通类多了一些抽象方法而已。若一个需求既可以使用抽象类也可使用接口优先使用接口。

  • 抽象类仍然是单继承局限。

  • 抽象类虽然没法直接实例化对象,子类仍然满足is a原则,子类和抽象父类之间仍然满足“继承树"的关系 Person 对于 China Sharp 对于 Cycle

接口(interface)

接口的两种表示场景

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

“游泳” -> 能力或者行为, Person满足有用借口,Dog也能满足游泳接口,Duck也能满足游泳接口

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

"USB接口,5G标准


  • 接口中只有全局常量和抽象方法 -> 更加纯粹的抽象概念。其他东西通通没有
  • 接口使用关键字interface声明接口,子类使用implements实现接口。
  • 一个类可以实现多个接口,但是只能继承一个抽象类
  1. USB接口:表示一种规范

    package interface_test.usb;
    //接口使用interface关键字定义:只有全局常量和抽象方法
    public interface USB {
        //插入
        public abstract void plugIn();
        //工作
        public abstract void work();
    }
    
  • 子类使用implements实现接口,必须覆写所有的抽象方法
package interface_test.usb;

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

    @Override
    public void work() {
        System.out.println("键盘正常工作");
    }
}

鼠标、键盘外设都属于USB接口的子类

小问一下

public class Computer {
    public void fun(USB usb) {
        usb.plugIn();
        usb.work();
    }
}

此时为何方法的参数用的是USB接口引用?

fun方法就模拟电脑的USB插口

如果fun方法参数Mouse会怎么样?

答:对于电脑的使用者生产者来说,根本不关心到底哪个具体设备插入到我的电脑上,只要这个设备满足了USB接口,都能被电脑识别,就可以实现一个接口可以接受无数种设备,只要这个设备满足USB接口,都可以插入到电脑且被电脑识别。即兼容所有的USB子类对象

fun(Mouse mouse) =》这个插口只能插鼠标,键盘都无法识别,这是两个毫无关系的类

public class Computer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Mouse mouse = new Mouse();
        //插入鼠标 ok
        computer.fun(mouse);
        KeyBoard keyBoard = new KeyBoard();
        computer.fun(keyBoard);
        Campera campera = new Campera();
        computer.fun(campera);
    }

    public void fun(USB usb) {
        usb.plugIn();
        usb.work();
    }
}

  • 接口表示能力

接口允许多实现,一个类可能具备多个能力,同时实现多个父接口,若实现多个父接口,子类普通类,需要覆写所有的抽象方法

public class Dog implements IRun,ISwim{
    @Override
    public void run() {
        System.out.println("狗娃子在跑");
    }

    @Override
    public void swim() {
        System.out.println("狗娃子在狗刨");
    }
}

public class Dog implements IRun,ISwim表示子类同时实现了多个父接口

  • 由于接口中只有全局常量和抽象方法,因此接口中

public abstract => 抽象方法

static final => 常量

全都可以省略!!(必须在接口中)

//表示能飞
public interface IFly {//接口中只有全局常量和抽样方法
    String test = "nb";//全局常量 等价于 static final String test
    //private protected 不允许出现在接口中,public多余,所以只有默认的了
    public abstract void fly();//public 和 abstract多余
}

String test = "nb";等价于static final String test = "nb";

public abstract void fly();等价于void fly();写上多余,有警告

instanceof关键字

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

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

Animal animal1 = new Animal();
Animal animal2 = new Dog();
System.out.println(animal1 instanceof Dog);//false
System.out.println(animal2 instanceof Dog);//true

使用instanceof关键字的返回值搭配的分支语句进行类型转换

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值