Java面向对象编程

本文详细讲解了Java中的包结构、访问修饰符(private、default、protected、public)、继承规则、final关键字的应用、多态(包括向上转型和方法重写)、以及抽象类和接口的概念。通过实例演示,帮助理解如何利用这些特性进行高效编程和设计模式。
摘要由CSDN通过智能技术生成

访问修饰符
从小到大依次为
private(私有,当前类的内部可见)< default(啥都不写就是包权限,当前包的内部可见,不包含子包,同级目录下可见)< protected(继承,不同包中的有继承关系的类之间可见)< public(当前的项目可见)

src下new package 创建包,包命名使用全小写
包最大意义在于可以重名
包访问权限指的是权限啥都没写。默认就是包访问权限
对于包访问权限来说,只有当前的包的内部可见(子包,子文件夹都不可见)
Java中的“包”就是操作系统的文件夹
声明一个包使用package关键字
若存在多个文件夹的嵌套,使用”.”分隔
package www.bit.java.test
import www.Animal 导入www包下的Animal这个类
Import java.util.Data 此时这个Data表示的是java.util这个包下的Data类

若此时还要用到这个包中的其他类,不用一行一行写,直接
import.java.util.*;

import.*可能会出现歧义,若此时我们程序中用到了两个相同的类
import只能导入包中的类,无法导入包
在这里插入图片描述

当出现上述情况,有两种解决方法
1.指名是哪个包下的类

java.util.Date date = new java.util.Date();
java.util.Date date1 = new java.util.Date();

2.指名是导入哪个包下的类

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

静态导入

import static 可以导入包中的静态方法和静态属性(不推荐使用)
不推荐使用类名称、方法名称/属性名访问类中的静态域

import static java.lang.System.*;
//导入System这个类下的所有静态方法和属性
import static java.lang.Math.*;
//导入Math这个类下的所有静态方法和属性
public static void main(String[] args) {
    Date date = new Date();
    Date date1 = new Date();
    int ret = Math.max(10,20);
    int ret1 = max(10,20);//当导入Math后就可以直接调用max
    out.println(ret);//当导入了System后就能直接使用
    out.println(ret);
}

常见的系统包

java.lang:JDK的基础类,System、String、Object都在这个包下,JDK1.1后这个包的所有类自动导入
java.lang.reflect:反射开发包
java.util 工具包(集合类都在这个包下,Arrays,Linked List,Hash Map)
java.io:i/o开发包,文件读取和写入
java.net:网络编程开发包,Socket
java.sql:数据库开发用到的包

封装:使用Private将属性进行封装(这个属性只在当前类的内部可见,对外部隐藏)
保护性 易用性(通过程序对外提供的方法来操作属性)
易用性类似于汽车的启动,无需知道汽车的启动原理,只需要按按钮就能够一键启动

继承

public class Bird extends Animal {//子类Bird继承了父类Animal
}//bird is an Animal

不能为了省略代码就简单使用继承,要使用继承必须满足is a 关系
不满足is a 关系的类之间不能使用继承
Person extends Dog 这样就是不行的
Person is not a Dog

继承的规则

a.要使用继承,前提必须满足类之间的is a关系
b.一个子类只能使用extend继承一个父类(单继承)
c.子类会继承父类所有的属性和方法,显示继承(public属性和方法能够直接使用),隐式继承(private属性和方法),子类虽然也继承了private属性和方法,但是不能够直接使用,需要通过get和set方法使用

Java中不允许多重继承,extend后面只能有一个父类,允许多层继承,不能当儿子可以当孙子
在这里插入图片描述

private int age;//隐式继承,子类无法直接使用该属性,必须通过父类提供的方法来操作

静态方法和变量与成员方法和变量的区别在于究竟是通过子类对象来使用,还是通过类名称来使用

关于protected访问权限

protected作用域:不同的包中有继承关系的子类可见
在animal包中
在这里插入图片描述
在person这个包中
在这里插入图片描述
不在同一个包中且不是Animal的子类,那么Animal中的name对于这个类就是不可见的
protected权限>default权限
包权限中可见的范围,protected来说一定可见
同包下的没有关系的类之间以及不同包中有继承关系的类之间可见,不包含子包

要产生一个子类对象,默认优先产生一个父类对象
在Animal包中

public Animal(){
    System.out.println("1.先调用父类的构造方法");
}

在子类Person中

public Person(){
    System.out.println("2.产生子类对象,再产生子类对象的构造方法");
}

public static void main(String[] args) {
    Person person = new Person();
}

执行结果:
在这里插入图片描述
可以看出,当调用new Person无参构造产生子类对象之前,默认调用父类的构造方法产生父类对象,然后才会执行子类的构造方法

习题
在B类中

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

在D类中

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结束");
    }
}

求执行的顺序?
执行结果如下:
3.B的静态块
6.D的静态块
7.main开始
2.B的构造体
1.B的构造方法
5.D的构造块
4.D的构造方法
2.B的构造体
1.B的构造方法
5.D的构造块
4.D的构造方法
8.main结束
解析:由于main存在于D子类中,若JVM要调用main,首先要加载主类,执行主类的静态块,D继承了B,先加载父类才加载子类。3(父类的静态块)->6(才加载子类的静态块)
类加载结束后,进入主方法7
要产生子类对象,先要产生父类对象,先调用构造块,下来才是构造方法
先调用父类的构造块2,调用父类的构造方法1=>父类对象产生完毕
再调用子类的构造块5,调用子类的构造方法4=>子类对象产生完毕

此时new了两次所以要再重复执行一次
调用父类的构造块2,调用父类的构造方法1=>父类对象产生完毕
调用子类的构造块5,调用子类的构造方法4=>子类对象产生完毕
最后走到8主方法结束

super关键字

super修饰属性表示从父类中寻找同名属性
super修饰方法分为:
1.修饰构造方法
2.修饰普通方法
super先从父类中寻找同名属性,若不存在再向上找
this直接从当前类中找同名属性,若不存在再向上找
在这里插入图片描述
在父类中

public class Person {
    public String name = "person";
}

在子类中

public class China extends Person {
    public String name = "china";
    public void fun(){
        System.out.println(this.name);//再访问子类成员变量时,推荐使用this,也可省略
    }

   public static void main(String[] args) {
        China china = new China();
        china.fun();//此时打印的是china
    }
}

当有继承关系时,this关键字优先从当前类中寻找同名属性,若未找到,继续向上寻找父类是否有同名属性
直接使用this默认从当前类中寻找

只要产生一个子类对象,默认先产生父类对象,若父类对象还有继承,则继续向上产生祖类对象

public class Person {
    public Person(){
        System.out.println("person的构造方法");
    }
}
class Chinese extends Person{
    public Chinese(){
        System.out.println("chinese的构造方法");
    }
}
class Japanese extends Chinese{
    public Japanese(){
        System.out.println("Japanese的构造方法");
    }
    public static void main(String[] args) {
        Japanese japanese = new Japanese();
    }
}

执行结果:
在这里插入图片描述

super修饰构造方法

super(父类构造方法的参数)
super();//直接调用父类的无参构造,可写可不写
若父类中不存在无参构造,则子类的构造方法的首行必须使用Super(有参构造)

class Japanese extends Chinese{
    public Japanese(){
        //super(); 首行默认使用父类的构造方法
        System.out.println("Japanese的构造方法");
    }

当父类person中没有无参构造,子类构造方法的首行必须使用super(有参构造)

public class Person {
    public Person(String name){
        System.out.println("person的构造方法");
    }
}
class Chinese extends Person{
    public Chinese(){
        super("China");//父类没有无参构造,首行必须使用super(有参构造)
        System.out.println("chinese的构造方法");
    }
}

super修饰普通方法,修饰属性,直接从父类中找同名方法
如果子类和父类的方法同名,想从父类中寻找方法,使用super.方法名即可

this可以直接使用,表示当前对象的引用
super可以访问父类的属性和方法,但是super不能直接使用

System.out.println(this);//true
System.out.pritnln(super);//error

在一个构造方法中无法显式同时使用this()和super(),可以隐式

public Chinese(String name){
    //super(); 默认会调用父类的构造方法可不写
    this();
    System.out.println("chinese的构造方法");
}

在这里插入图片描述

final关键字

final:1.修饰属性表示属性值不能改变,常量值定义无法修改
2.修饰类,表示这个类不能被继承
在这里插入图片描述
final class Animal=>Animal无法被继承

类和类之间除了继承关系还有组合关系
Dog extend Animal
类A is 类B =>类A继承于类B

组合关系 has a
class School{
Student student;
Teacher teacher;
}
School has a teacher
School has a student

多态

多态:一个引用可以表现出多种行为/特性=>多态性
向上转型:
Dog dog = new Dog();//最常见的
类名称 类引用 = new 该类对象();

Animal animal = new Dog();//向上转型
父类名称 父类引用 = new 子类对象()
子类 天然 is a 父类
Dog is an Animal

向上转型发生在有继承关系的类之间
向上转型不一定非得是子类,也可以是子类的子类

Animal animal = new Bird();//bird is an animal
Bird bird = new Duck();//duck is a bird
Animal animal1 = new Duck();//duck is an animal

向上转型的最大意义在于参数统一化,降低使用者的使用难度
1.作为类的使用者
fun方法接收Animal以及其子类的对象作为参数,假设现在没有向上转型,Animal有多少子类,我就得重载多少次fun方法
2.作为类的使用者,程序的使用者
没有向上转型,又要使用fun方法的话,我就得了解Animal以及其子类的所有对象,我就知道我到底调用的是谁
子类是天然的父类,用父类就能指向所有的子类=>向上转型产生
通过Animal的最顶层的父类引用,指向所有子类对象

 public class Animal {
    public static void main(String[] args) {
        fun(new Animal());
        fun(new Bird());
        fun(new Duck());
        //new一个Duck就能直接使用fun方法

    }
    public static void fun(Animal animal){
        //最顶端的父类Animal就能指代所有子类对象
        System.out.println("123");
    }
}

有向上转型之后,最顶端的父类引用就能指代所有子类对象,当Animal有一个新的子类时,非常容易扩展

方法重写

方法重写(override):发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他完全相同的方法(特殊情况返回值可以不同)

在Duck类中

public class Duck extends Bird{
    public void fun(){
        System.out.println("duck的fun方法");
    }
}

在Bird类中

public class Bird extends Animal{
    public  void fun(){
        System.out.println("bird的fun方法");
    }
}

在Animal类中

 public class Animal {
    public static void main(String[] args) {
        Animal animal2 = new Bird();
        Animal animal1 = new Animal();
        Animal animal3 = new Duck();
        animal1.fun();
        animal2.fun();
        animal3.fun();
    }
    public  void fun(){
        System.out.println("Animal的fun方法");
    }
}

执行结果:
在这里插入图片描述
如果在子类Duck和Bird中没有重写fun方法,就会调用Animal中的fun方法
在这里插入图片描述
可以看出,如果子类重写了父类的方法,当传入不同的对象时,表现出了不同的fun方法=>多态性
同一个引用(变量名称),同一个方法名称根据对象的不同表现出了不同的行为=>多态性

Animal animal = new Bird();
不用去看前半部分,只需要看new的对象是否重写了相关方法,若重写了,那么调用的一定是当前重写后的方法

子类若没有重写父类的方法,则向上搜寻第一个重写了该方法的父类,直接调用
在这里插入图片描述
若c中没有test则一层一层向上查找,找到就调用

当发生重写时,子类权限必须>=父类权限才能重写
private<default<protected<public
private权限不包括在内

Java中有一个注解@Override,使用这个注解在重写方法之前,帮你校验您的方法重写是否符合规则

在父类中

 public class Animal {
    public static void main(String[] args) {
        Animal animal = new Bird();
        animal.add();
    }
    private void add(){
        System.out.println("animal的add方法");
    }
}

在子类中

public class Bird extends Animal{
    public void add(){
        System.out.println("bird的add方法");
    }
}

执行结果:
在这里插入图片描述
虽然此时Bird重写了add方法,但是父类的add方法是private方法,出了父类都不知道有这个类,所以结果仍然是父类的add方法
例题:

public class Person {

    private void test(){
        System.out.println("1.person类的test方法");
    }
    public void fun(){
        this.test();
    }

}
public class Student extends Person{
    public void test(){
        System.out.println("2.student的test方法");
    }

}
public class Test {
    public static void main(String[] args) {
        new Student().fun();
    }
}

执行结果为多少?
1.person类的test方法
解析:1当子类Student调用fun方法,由于子类并没有覆写这个方法,所以调用父类的fun方法
2.父类的fun方法中的this.test(),此时调用的仍然是父类的方法,因为父类的test方法是private定义的,子类无法覆写,所以结果是1.person类的test方法

注意:不能重写static方法,多态的本质就是因为调用了不同的子类为”对象”,这些子类对象所属的类覆写了相应的类,才能表现出不同的行为,static与对象无关

在这里插入图片描述
对象多态性一共可以发生在三个位置
方法传参->使用最多的
在这里插入图片描述
向上转型
类名称 引用名称 = new 类实例();
引用名称.方法名称();
能通过”.”访问的方法类名称说了算
能访问的这些方法都必须在类中定义,编译器会先在类中查找是否包含指定方法
至于这个方法到底表现出来是什么样子,实例所在的方法说了算
在这里插入图片描述
例题:

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();
    }
}

求执行结果是多少?
在这里插入图片描述
4.给num赋予值10
当走到第四步之前就已经打印出了结果:D.fun,num=0;
Animal

向下转型
想调用子类扩展的方法就需要用到向下转型
子类名称 子类引用 = (子类名称)父亲引用

public class Dog extends Animal{
    public void play(){
        System.out.println("Dog的play方法");
    }
}
public class Animal {
   public static void main(String[] args) {
       Animal animal = new Dog();
       Dog dog = (Dog)animal;//当animal想要用到dog的方法就需要向下转型
       //animal本质上仍然是dog,但是需要脱掉animal的外衣还原为dog类引用
   }

Animal animal = new Dog();//将Dog类向上转型为animal类
Dog dog = (Dog)animal;//将animal向下转型为dog类
dog.play();//转为dog后就能使用dog类的方法
Animal animal1 =dog;//将dog再转为animal类
要发生向下转型必须要先发生向上转型
当调用Animal类的对象直接向下转型时,会有风险
在这里插入图片描述
当发生向下转型会有风险,类型转换异常,使用instanceof关键字
引用名称instancof类=>返回布尔值,表示该引用指向的本质是不是该类的对象

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

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

Animal animal = new Animal();
Animal animal1 = new Dog();
if (animal instanceof Dog){
    Dog dog = (Dog) animal;
    System.out.println(animal+"转型成功");
} else {
    System.out.println(animal+"不是指向Dog类型引用");
}
if (animal1 instanceof Dog){
    Dog dog = (Dog)animal1;
    System.out.println(animal1+"转型成功");
} else {
    System.out.println(animal1+"不是指向Dog类型引用");
}

啥时候发生向上转型,方法接收一个类和当前类的子类,参数指定为相应父类引用,发生的就是向上转型
只有某个特殊的情况下,需要使用子类扩展方法,才需要将原本向上转型的引用向下转型还原为子类引用
在这里插入图片描述
向下转型:我们现在已知的只有一个父类引用,但是我要使用这个子类的扩展方法

子类名称 子类引用 =(子类名称)父类引用
Animal animal =new Dog();
Dog dog = (Dog)animal;
向上转型:90%以上都是向上转型来创建对象
参数统一化,使用一个共同的父类引用可以接收所有子类实例

proteced权限:子类/当前类的内部/同包中的不同类

class SubType extends Base {
    public void fun(){
        System.out.println(name);//在成员方法中直接调用name是可以的
        Base base = new Base();
        System.out.println(base.name);
        //此时虽然在子类中,使用的是Base的引用直接访问name属性,是不行的
    }
}
public class Base {
    protected String name = "杰哥";
}

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

抽象类

若需要强制要求子类覆写方法,用到抽象类
其实现实生活中有很多的抽象类,这些类都是概念化,没法具体到某个实例,没法具体到某个实例,描述一类对象共同的属性和行为 人类—>抽象类
抽象类是普通类的超集,只是比普通类多一些抽象方法而已
抽象方法所在的类必须是抽象类,子类(普通类)若是继承了抽象类,必须覆写所有抽象方法
普通类有的,抽象类都会有

Java中定义抽象类或者抽象方法使用abstract关键字
1.抽象方法所在的类必须使用abstract声明为抽象类
抽象方法指的是使用abstract关键字声明,只有函数声明,没有函数实现方法,称为抽象方法

public abstract class Sharp {//一个类中若存在抽象方法,必须使用abstract抽象类
    public abstract void print();//抽象类中没有具体实现,延迟到子类实现
}

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

在这里插入图片描述
抽象方法:使用关键字abstract声明只有方法声明没有方法体(“{}”)的方法
Java中,没有方法体的方法不仅有抽象方法,还有本地方法
Public final native void notifyAll();//本地方法,不是抽象方法,这个方法的具体实现由JVM实现,JVM是C++写的,本地方法指的是调用了C++中的同名方法

2.若一个类使用abstract声明为抽象类,无法直接通过该实例化对象,哪怕该类中一个抽象方法都没有,当一个类是抽象类,不管他有没有抽象方法,这个类的本身就是一个抽象概念,无法具体到某个特定的实例
在这里插入图片描述
只能通过子类向上转型变为抽象父类的引用

3.子类(普通类)继承了抽象类,就必须强制覆写抽象类中的所有抽象方法,也必须满足单继承,一个子类只能extends一个

public abstract class A {
    abstract void A();
}
abstract class B extends A{//B的抽象类,可以选择性覆写父类的抽象方法
    abstract void B();
}
class C extends B{//C是普通类,必须覆写B中所有的抽象方法,由于B没有覆写A的抽象方法,所以C要覆写A的抽象方法

    @Override
    void A() {
        
    }

    @Override
    void B() {

    }
}

3.抽象类是普通类的超集(普通类有的内容,抽象类全都有),只是比普通类多一些抽象方法而已,抽象类虽然不能实例化对象,但是也可以存在构造方法,子类在实例化时,仍然遵从继承规则,先调用父类(抽象类)的构造方法,而后调用子类的构造方法

public abstract class BaseTest {
    public BaseTest(){
        this.print();//此时调用的是子类中的print,因为子类调用的父类构造方法
    }
    abstract void print();
}
class Fun extends BaseTest{
    void print(){
        System.out.println("123");
    }

    public static void main(String[] args) {
        new Fun();
    }
}

执行结果:123

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

抽象类虽然无法直接实例化对象,子类仍然满足is a 原则,子类和抽象父类仍然满足继承树关系
Person 对于 Chinese
Sharp 对于Cycle

接口

一般来说,接口的使用表示两种场景
1.接口表示具备某种能力/行为,子类实现接口时不是is a,而是具备这种行为或者能力
“游泳”->能力或者行为,Person满足游泳接口,Dog也能满足游泳接口,Duck也能满足游泳接口

2.接口表示,一种规范或者标准
像是“USB”接口,“5G”标准
鼠标、键盘外设都属于USB接口的子类
子类使用implements实现接口,必须覆写父类的所有的抽象方法
接口只有全局常量和抽象方法->更加存粹的抽象概念,其他东西统统没有
接口使用interface关键字定义,只有全局变量(1%)和抽象方法(99%)

表示某种规范

public interface USB {
    public abstract void plugIn();//插入
    public abstract void work();//工作
}
public class Keyboard implements USB {//鼠标属于USB接口的子类
    @Override
    public void plugIn() {
        System.out.println("安装键盘驱动中");
    }

    @Override
    public void work() {
        System.out.println("键盘开始工作");
    }
}
public class Mouse implements USB {//鼠标是USB接口的子类
    @Override
    public void plugIn() {
        System.out.println("安装鼠标驱动");
    }

    @Override
    public void work() {
        System.out.println("鼠标开始工作");
    }
}
public class Computer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Keyboard keyboard = new Keyboard();//插入键盘
        Mouse mouse =new Mouse();//插入鼠标
        computer.fun(keyboard);
        computer.fun(mouse);
    }
    public void fun(USB usb){
        usb.plugIn();
        usb.work();
    }
}

开闭原则:所有设计模式的核心思想:程序应当对外扩展开放,对外修改关闭,方便扩展,不能影响已经写好的程序

接口表示能力:接口允许多实现,一个类可以具备多个能力,同时实现多个接口,若实现多个父类接口,子类必须覆写所有抽象方法

public interface IFly {
    public abstract void fly();
}
public interface IRun {
    public abstract void run();
}
public interface ISwim {
    public abstract void swim();
}
public class Dog implements IRun,ISwim{
    @Override
    public void run() {
        System.out.println("狗会跑");
    }

    @Override
    public void swim() {
        System.out.println("狗会游泳");
    }
}
public class Pig implements IRun{
    @Override
    public void run() {
        System.out.println("猪会跑");
    }
}
public class Duck implements IFly,ISwim,IRun{
    @Override
    public void fly() {
        System.out.println("鸭子会飞");
    }

    @Override
    public void run() {
        System.out.println("鸭子会跑");
    }

    @Override
    public void swim() {
        System.out.println("鸭子会游泳");
    }
}
public class Test {
    public static void main(String[] args) {
        IFly iFly = new Duck();//接口不能实例化对象,需要向上转型
        ISwim iSwim = new Duck();
        IRun iRun = new Duck();
        iFly.fly();
        iSwim.swim();
        iRun.run();
        IRun iRun1 = new Dog();
        ISwim iSwim1 = new Dog();
        iRun1.run();
        iSwim1.swim();
        IRun iRun2 = new Pig();
        iRun2.run();
    }
}

执行结果
在这里插入图片描述
由于接口中只有全局常量和抽象方法,因此接口中的
public abstract() 可以省略
Static final 可以省略
在接口声明中,这些关键字都可以不写,只保留最核心的方法返回值,方法参数列表,名称即可
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值