Java 面向对象总结

Java 面向对象总结

图解

特点

面向对象具有封装、继承、多态、抽象等特性。

类和对象

类是对一类事物的描述,是抽象的。

对象是一类事物的实例,是具体的。

类是对象的模板,对象是类的实体。

类是封装对象的属性和行为的载体。

成员变量和局部变量

成员变量的默认值

数据类型 默认值
基本类型 整数(byte,short,int,long) 0
浮点数(float,double) 0.0
字符(char) ‘\u0000’
引用类型 布尔(boolean) false
数组,类,接口 null

权限修饰符

Java 中主要有 private、protected、public 和 默认访问权限四种。

public 修饰符,具有最大的访问权限,可以访问任何一个在CLASSPATH 下的类、接口、异常等。

protected 修饰符,主要作用就是用来保护子类,子类可以访问这些成员变量和方法,其余类不可以。

default 修饰符,主要是本包的类可以访问。

private 修饰符,访问权限仅限于本类内部,在实际开发过程中,大多数的成员变量和方法都是使用 private 修饰的。

修饰符 本类 本包 外包子类 外包
public √ √ √ √
protected √ √ √
default √ √
private √

static 关键字

随着类的加载而加载,优先于对象存在。(在静态方法中是没有this关键字的,this是随着对象的创建而存在)。

被类的所有对象共享,可以通过类名调用,也可以通过对象名调用,推荐使用类名调用。

静态变量存储于方法区的静态区,随着类的加载而加载,随着类的消失而消失。

this和super

this是对当前对象的引用,是运行期间的对象本身。

super是直接父类对象的引用,可以访问弗雷中被子类覆盖的属性和方法。

this和super调用构造函数

this

this关键字在构造函数中的应用:构造函数间调用,只能使用this进行互相调用,this函数不能用在一般函数间。

this语句(不是this关键字)只能定义在构造函数的第一行。

class Person{

private String name;
private int age;

Person(String name){
this.name = name;
}

Person(String name, int age){
this(name);
this.age = age;
}

}

super

在对子类对象进行初始化时,父类构造函数也会运行,是因为子类的构造函数默认第一行有一条隐式的语句super(),super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。

当父类中没有空参数的构造时,系统会报错,所以必须手动指定子类引用父类的哪个非空的构造函数。

如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式进行指定。

class Fu{

int num;

Fu(){
this.num = 60;
}

Fu(int x){
this.num = x;
}

}

class Zi extends Fu{

Zi(){
// super();
// super(30);
}

Zi(int x){
this();
}
}

结论(子类的实例化过程):子类中所有的构造函数,默认都会访问父类中的空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式的super(),当父类中没有空参数的构造函数时,子类必须手动通过super语句或者this语句形式来指定要访问的父类中的构造函数。子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少要有一个构造函数会访问父类中的构造函数(没写也会有默认的)。

封装

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。

封装的步骤

  1. 使用 private 关键字来修饰成员变量。
  2. 对需要访问的成员变量,提供对应的一对 getXxx 方法 、 setXxx 方法。

标准代码——JavaBean

JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的 set 和 get 方法。

public class Student {
//成员变量
private String name;
private int age;
//构造方法
public Student() {}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
//成员方法
publicvoid setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}

继承

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

需要注意的是 Java 不支持多继承,但支持多重继承

继承的特性

1、子类拥有父类非 private 的属性、方法
2、子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
3、子类可以用自己的方式实现父类的方法
4、提高了类之间的耦合性

public class Animal{

private String name;
private int id;

public Animal(String name, int id){
this.name = name;
this.id = id;
}

public void eat(){
System.out.println(name + "正在吃");
}

}

public class Pig extends Animal{

public Pig(String name, int id){
super(name, id);
}
}

多态

概念:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。

简单的说:就是用基类的引用指向子类的对象。

java引用类型有两个:

  编译时类型

  编译时类型由声明该变量时使用的类型决定

  运行时类型

  运行时类型由实际赋给该变量的对象决定

静态多态和动态多态的区别:

多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

多态存在的三个必要条件

  1. 要有继承
  2. 要有重写
  3. 父类引用指向子类对象
class Animal{
public int month = 2;
public void eat(){
System.out.println("动物吃东西");
}

}

class Dog extends Animal{
public int month = 3;

public void eat() {
System.out.println("小狗吃肉");
}

public void sleep() {
System.out.println("小狗睡午觉");
}
}

class Cat extends Animal{
public int month = 4;

public void eat() {
System.out.println("小猫吃鱼");
}
}

public class Test {
public static void main(String[] args){
Animal a = new Dog();
Animal b = new Cat();
a.eat();
System.out.println(a.month);
//下面代码编译时会出错
//a.sleep();
b.eat();
System.out.println(b.month);

}
}

运行结果:

小狗吃肉
2
小猫吃鱼
2

上例中main方法中注释了a.sleep(),由于a的编译时类型为Animal,而Animal类中没有sleep方法,因此无法在编译时调用sleep方法。

总的来说:

引用变量在编译阶段只能调用编译时类型所具有的方法,但运行时则执行他运行时类型所具有的方法。

对象类型的转换

父子对象之间的转换分为了向上转型和向下转型,它们区别如下:

  • 向上转型 : 通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换
  • 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换

向上转型

class A {
public void print() {
System.out.println("A:print");
}
}

class B extends A {
public void print() {
System.out.println("B:print");
}
}

public class Test{
public static void main(String args[])
{
A a = new B(); //通过子类去实例化父类
a.print();
}
}
// 结果
// B:print

可以看到打印的是class B的print,这是因为我们通过子类B去实例化的,所以父类A的print方法已经被子类B的print方法覆盖了.从而打印classB的print.

这样做的意义在于:

  • 当我们需要多个同父的对象调用某个方法时,通过向上转换后,则可以确定参数的统一.方便程序设计(参考下面示例)
class A {
public void print() {
System.out.println("A:print");
}
}

class B extends A {
public void print() {
System.out.println("B:print");
}
}

class C extends B {
public void print() {
System.out.println("C:print");
}
}

public class Test{
public static void func(A a)
{
a.print();
}

public static void main(String args[])
{
func(new B()); //等价于 A a =new B();
func(new C()); //等价于 C c =new C();
}
}
// 结果
// B:print
// C:print

PS:向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.

向下转型

在java中,向下转型则是为了,通过父类强制转换为子类,从而来调用子类独有的方法(向下转型,在工程中很少用到).

为了保证向下转型的顺利完成,在java中提供了一个关键字:instanceof,通过instanceof可以判断某对象是否是某类的实例,如果是则返回true,否则为false,instanceof使用如下:

A a = new B();                 //向上转型 (B类是A的子类)

a instanceof A; //返回true.
a instanceof B; //返回true
a instanceof C; //返回false

我们便来分析向下转型的意义.

class A {
public void print() {
System.out.println("A:print");
}
}

class B extends A {
public void print() {
System.out.println("B:print");
}
public void funcB(){
System.out.println("funcB");
}
}

class C extends A {
public void print() {
System.out.println("C:print");
}
public void funcC(){
System.out.println("funcC");
}
}

public class Test{
public static void func(A a)
{
a.print();
if(a instanceof B)
{
B b = (B)a; //向下转型,通过父类实例化子类
b.funcB(); //调用B类独有的方法
}
else if(a instanceof C)
{
C c = (C)a; //向下转型,通过父类实例化子类
c.funcC(); //调用C类独有的方法
}
}

public static void main(String args[])
{
func(new A());
func(new B());
func(new C());
}
}
// 结果
// A:print
// B:peint
// funcB
// C:print
// funcC

从上面打印可以看到,我们成功通过向下转型来调用B类和C类独有的方法.

方法重写和方法重载

方法重写Override

子类中出现了和父类中方法声明一模一样的方法。与返回值类型有关,返回值是一致(或者是子父类)的。

方法重载Overload

本类中出现的方法名一样,参数列表不同的方法。与返回值类型无关。

Object类

Java中的所有类最终都继承于Object。

equals()方法

该方法用于比较两个对象,如果这两个对象引用指向的是同一个对象,那么返回true,否则返回false。

clone()方法

默认的clone方法是浅拷贝模式。所谓浅拷贝,指的是对象内属性引用的对象只会拷贝引用地址,而不会将引用的对象重新分配内存。深拷贝则是会连引用的对象也重新创建。

toString()方法

这个方法的使用频率非常高,用于返回一个可代表对象的字符串。

默认返回格式如下:对象的class名称 + @ + hashCode的十六进制字符串

finalize()方法

这个方法用于在GC的时候再次被调用,如果我们实现了这个方法,对象可能在这个方法中再次复活,从而避免被GC回收。

通常情况下,我们不需要自己实现这个方法。

抽象类和接口

抽象类

没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。

抽象类:abstract class 类名 {}

抽象方法:public abstract void eat();

注意:

​ 1、抽象类不能实例化,由具体的子类实例化。其实这也是多态的一种,抽象类多态。

   2、抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

   3、抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

   4、抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
public class Cat extends Animal{

@Override
public void eat() {
System.out.println("我是猫,我吃的是猫粮呀");
}

@Override
public void sleep() {
System.out.println("我是猫,我比你们人类睡的时间短!");
}

}

public class Tiger extends Animal {

@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("我是老虎,我要吃大鱼大肉!!!");
}

@Override
public void sleep() {
// TODO Auto-generated method stub
System.out.println("我是老虎,每天必须睡够24个小时!!!!");
}

}

抽象类的用处:

虽然也要在Cat和Tiger中定义sleep和eat这两个方法,看似代码上没有太多简化。但是这背后却隐藏着一个规范问题:也就是“是不是”的问题。cat和person都“是”animal,所以就必须继承animal里面的方法。相当于提供了一个大的体制。

接口

接口就是一个规范和抽象类比较相似。它只管做什么,不管怎么做。通俗的讲,借口就是某个事物对外提供的一些功能的声明,其定义和类比较相似,只不过是通过interface关键字来完成。

其中重要的几个知识点:

1.接口中的所有属性默认为:public static final **;

2.接口中的所有方法默认为:public abstract **;

3.接口不再像类一样用关键字 extends去“继承”,而是用 implements 去“实现”,也就是说类和接口的关系叫做实现,(例如:A类实现了B接口,那么成为A为B接口的实现类。而类与类之间的继承的话,叫做A类继承了B类,其中B类即为A类的父类)。实现接口与类的继承比较相似。

public interface Sleep {

public static int a = 1;
public static int b = 2;

public void ioSleep(int i);
}
public interface Eat {
public abstract void ioEat();
}
public interface Study {
public void ioStudy();
}
public class Cat implements Sleep,Eat{

@Override
public void ioSleep(int i) {
System.out.println("我是猫,我每天都不用睡觉!!!");
}

@Override
public void ioEat() {
System.out.println("我是猫,我吃猫粮!!!");
}
}
public class Person implements Sleep,Eat,Study{

@Override
public void ioStudy() {
System.out.println("我是人,我每天都要学习");
}

@Override
public void ioEat() {
System.out.println("我是人,我要吃大鱼大肉还要喝酒");
}

@Override
public void ioSleep(int i) {
System.out.println("我是人,我每天要睡24小时");
}
}

所以也就可以列出抽象类和接口的几点区别:

​ 1.抽象类描述的是“是不是”的问题,而接口描述的是“有没有”的问题;

​ 2.在Java中类的继承是“单继承”,可以“多对一”,但是不允许“一对多”。而一个类却可以同时实现多个接口;

final关键字

final修饰特点

修饰类,类不能被继承

修饰变量,变量就变成了常量,只能被赋值一次

修饰方法,方法不能被重写

内部类

jdk 推出了「内部类」的概念,当然,内部类不仅仅弥补了 Java 不能多继承的一个不足,通过将一个类定义在另一个类的内部,也可以有效的隐藏该类的可见性,等等。

接口 + 内部类 = 多继承

在这之前,Java 的继承机制主要由接口和单根继承实现,通过实现多个接口里的方法,看似能够实现多继承,但是并不总是高效的,因为一旦我们继承了一个接口就必然要实现它内部定义的所有方法。

现在我们可以通过内部类多次继承某个具体类或者接口,省去一些不必要的实现动作。只能说 Java 的内部类完善了它的多继承机制,而不是主要实现,因为内部类终究是一种破坏封装性的设计,除非有很强的把控能力,否则还是越少用越好。

public class Father {
public String powerFul = "市长";
}

public class Mother {
public String wealthy = "一百万";
}
public class Son {
class Extends_Father extends Father{
}

class Extends_Mother extends Mother{
}

public void sayHello(){
String father = new Extends_Father().powerFul;
String mother = new Extends_Mother().wealthy;
System.out.println("my father is:" + father + "my mother has:" + mother);
}
}

显然,我们的 Son 类是不可能同时继承 Father 和 Mother 的,但是我们却可以通过在其内部定义内部类继承了 Father 和 Mother,必要的情况下,我们还能够重写继承而来的各个类的属性或者方法。

这就是典型的一种通过内部类实现多继承的实现方式,但是同时你也会发现,单单从 Son 来外表看,你根本不知道它内部多继承了 Father 和 Mother,从而往往会给我们带来一些错觉。所以你看,内部类并不绝对是一个好东西,它破坏了封装性,用的不好反而会适得其反,让你的程序一团糟,所以谨慎!

当然,并不是贬低它的价值,有些情况下它也能给你一种「四两拨千斤」的感觉,省去很多麻烦。下面我们看看几种不同的内部类类型。

从种类上说,内部类可以分为四类:普通内部类、静态内部类、匿名内部类、局部内部类。

成员内部类

普通内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象

public class InnerClassTest {

public int outField1 = 1;
protected int outField2 = 2;
int outField3 = 3;
private int outField4 = 4;

public InnerClassTest() {
// 在外部类对象内部,直接通过 new InnerClass(); 创建内部类对象
InnerClassA innerObj = new InnerClassA();
System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
System.out.println("其内部类的 field1 字段的值为: " + innerObj.field1);
System.out.println("其内部类的 field2 字段的值为: " + innerObj.field2);
System.out.println("其内部类的 field3 字段的值为: " + innerObj.field3);
System.out.println("其内部类的 field4 字段的值为: " + innerObj.field4);
}

public class InnerClassA {
public int field1 = 5;
protected int field2 = 6;
int field3 = 7;
private int field4 = 8;
// static int field5 = 5; // 编译错误!普通内部类中不能定义 static 属性

public InnerClassA() {
System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
System.out.println("其外部类的 outField1 字段的值为: " + outField1);
System.out.println("其外部类的 outField2 字段的值为: " + outField2);
System.out.println("其外部类的 outField3 字段的值为: " + outField3);
System.out.println("其外部类的 outField4 字段的值为: " + outField4);
}
}

public static void main(String[] args) {
InnerClassTest outerObj = new InnerClassTest();
// 不在外部类内部,使用:外部类对象. new 内部类构造器(); 的方式创建内部类对象
// InnerClassA innerObj = outerObj.new InnerClassA();
}
}

这里的内部类就像外部类声明的一个属性字段一样,因此其的对象时依附于外部类对象而存在的,我们来看一下结果:

在这里插入图片描述

我们注意到,内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段。

Java 不允许成员内部类中定义静态的成员。

局部内部类

局部内部类就是在代码块中定义一个类,最典型的应用是在方法中定义一个类。例如:

public class Method {
private static String name;
private int age;

public void hello(){
class MyInnerClass{
public void sayHello(){
System.out.println(name);
System.out.println(age);
}
}
}
}

匿名内部类

匿名内部类,顾名思义,是没有名字的类,那么既然它没有名字,自然也就无法显式的创建出其实例对象了,所以匿名内部类适合那种只使用一次的情境,例如:

public class Test{
public static void main(String[] args){
Object obj = new Object(){
@Override
public String toString(){
return "hello world";
}
};
}
}

这就是一个典型的匿名内部类的使用,它等效于下面的代码:

public class MyObj extends Object{
@Override
public String toString(){
return "hello world";
}
}
public static void main(String[] args){
Object obj = new MyObj();
}

为了一个只使用一次的类而单独创建一个 .java 文件,是否有些浪费和繁琐?

在我看来,匿名内部类最大的好处就在于能够简化代码块。

显然,我们匿名内部类的构造器会调用相对应的父类构造器进行父类成员的初始化动作。而匿名内部类的本质也就这样,只是你看不到名字而已,其实编译器还是会为它生成单独的一份 Class 文件并拥有唯一的类名的。

其实你从编译器的层面上看,匿名内部类和一个实际的类型相差无几,它也能继承某个类并重写其中方法,实现某个接口的所有方法等。最吸引人的可能就是它无需单独创建类文件的简便性。

静态内部类

静态内部类通过对定义在外部类内部的类加上关键字「static」进行修饰,以标示一个静态内部类,例如:

public class OuterClass {
private static String name = "hello world";
private int age = 23;

public static class MyInnerClass{
private static String myName = "single";
private int myAge = 23;

public void sayHello(){
System.out.println(name);
//编译器报错提示:不可访问的字段 age
System.out.println(age);
}
}
}

首先,MyInnnerClass 作为一个内部类,它可以定义自己的静态属性,静态方法,实例属性,实例方法,和普通类一样。

此外,由于 MyInnerClass 作为一个内部类,它对于外部类 OuterClass 中部分成员也是可见的,但并全部可见,不同类型的内部类可见的外部类成员不尽相同,例如我们的静态内部类对于外部类的以下成员时可见的:

  • 静态属性
  • 静态方法

所以,我们上述的例子中,外部类 OuterClass 的实例属性 age 对于静态内部类 MyInnerClass 是不可见的。

那么 Java 是如何做到在一个类的内部定义另一个类的呢?

实际上编译器在编译我们的外部类的时候,会扫描其内部是否还存在其他类型的定义,如果有那么会「搜集」这些类的代码,并按照某种特殊名称规则单独编译这些类。正如我们上述的 MyInnerClass 内部类会被单独编译成 OuterClass$MyInnerClass.class 文件。

image

当然,这里的特殊命名规则其实就是:外部类名 + $ + 内部类名。

如果你想要在外部直接创建一个静态内部类的实例,也是被允许的。例如:

public static void main(String[] args){
//创建静态内部类实例
OuterClass.MyInnerClass innerClass = new OuterClass.MyInnerClass();
innerClass.sayHello();
}

当然,这样的操作一般也不被推荐,因为一个内部类既然被定义在某个外围类的内部,那它一定是为这个外围类服务的,而你从外部越过外围类而单独创建内部类的实现显然是不符合面向对象设计思想的。

静态内部类的应用场景其实还是很多的,但有一个基本的设计准则是,静态内部类不需要依赖外围类的实例,独立于外围类,为外围类提供服务。

单例设计模式

单例设计模式:保证类在内存中只有一个对象。

如何保证类在内存中只有一个对象呢?

(1)控制类的创建,不让其他类来创建本类的对象。private

(2)在本类中定义一个本类的对象 。Singleton s;

(3)提供公共的访问方式。 public static Singleton getInstance(){return s}

饿汉式的方法

public class Singleton {
//1.将构造方法私有化,不允许外部直接创建对象
private Singleton(){
}

//2.创建类的唯一实例,使用private static修饰
private static Singleton instance=new Singleton();

//3.提供一个用于获取实例的方法,使用public static修饰
public static Singleton getInstance(){
return instance;
}
}

懒汉式的方法

public class Singleton2 {
//1.将构造方式私有化,不允许外边直接创建对象
private Singleton2(){
}

//2.声明类的唯一实例,使用private static修饰
private static Singleton2 instance;

//3.提供一个用于获取实例的方法,使用public static修饰
public static Singleton2 getInstance(){
if(instance==null){
instance=new Singleton2();
}
return instance;
}
}

工厂方法模式

工厂方法是在设计模式中常用的一种模式,它属于设计模式的创造类型模式,主要用来创建对象。

使用场景

  • 创建对象需要大量重复的代码
  • 客户端(应用层)不依赖与产品类实例如何被创建、实现细节
  • 一个类通过其子类来指定创建那个对象

定义产品接口和工厂接口

/**
* 冰箱接口
*/
public interface IRefrigerator {
//获取品牌名
String getBrandName();

//获取价格
double getPrice();
}
/**
* 冰箱工厂接口
*/
public interface IRefrigeratorFactory {

IRefrigerator getIRefrigerator();
}

产品实现类

/**
* 海尔冰箱
*/
public class HaiErRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "海尔冰箱";
}

@Override
public double getPrice() {
return 5999;
}
}
/**
* 美的冰箱
*/
public class MeiDiRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "美的冰箱";
}

@Override
public double getPrice() {
return 2999;
}
}
/**
* 格力冰箱
*/
public class GeLiRefrigerator implements IRefrigerator {
@Override
public String getBrandName() {
return "格力冰箱";
}

@Override
public double getPrice() {
return 3999;
}
}

工厂实现类

/**
* 海尔冰箱工厂
*/
public class HaiErRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new HaiErRefrigerator();
}
}
/**
* 美的冰箱工厂
*/
public class MeiDiRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new MeiDiRefrigerator();
}
}
/**
* 格力冰箱工厂
*/
public class GeLiRefrigeratorFactory implements IRefrigeratorFactory {
@Override
public IRefrigerator getIRefrigerator() {
return new GeLiRefrigerator();
}
}

测试类

/**
* 测试类
*/
public class Test {

public static void main(String[] args) {
IRefrigeratorFactory refrigeratorFactory = new HaiErRefrigeratorFactory();
IRefrigeratorFactory refrigeratorFactory2 = new GeLiRefrigeratorFactory();
IRefrigeratorFactory refrigeratorFactory3 = new MeiDiRefrigeratorFactory();
IRefrigerator refrigerator = refrigeratorFactory.getIRefrigerator();

System.out.println("您购买了:" + refrigerator.getBrandName() + ",您需要支付:" + refrigerator.getPrice());
}
}

结果:

您购买了:海尔冰箱,您需要支付:5999.0

img

img

参考

https://www.cnblogs.com/liulyuan/p/10318732.html

https://images2015.cnblogs.com/blog/1081488/201704/1081488-20170425113401365-1070846569.jpg

https://www.processon.com/view/link/5c920ad7e4b0ed6b43088a87#map

https://www.cnblogs.com/lifexy/p/10812841.html

https://blog.csdn.net/luojun13class/article/details/83043100

https://blog.csdn.net/qq_40180411/article/details/81385518

https://www.cnblogs.com/haiyuexiaozu/p/10986510.html

https://blog.csdn.net/My_name_is_ZwZ/article/details/80001121

https://www.cnblogs.com/yangming1996/p/8869081.html

https://blog.csdn.net/qq_33427267/article/details/84767419

https://www.jianshu.com/p/4c7d0ee96094

Author: pangzibo243
Link: https://litianbo243.github.io/2019/10/02/Java-面向对象总结/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
支付宝打赏
微信打赏