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{ |
super
在对子类对象进行初始化时,父类构造函数也会运行,是因为子类的构造函数默认第一行有一条隐式的语句super(),super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。
当父类中没有空参数的构造时,系统会报错,所以必须手动指定子类引用父类的哪个非空的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式进行指定。
class Fu{ |
结论(子类的实例化过程):子类中所有的构造函数,默认都会访问父类中的空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式的super(),当父类中没有空参数的构造函数时,子类必须手动通过super语句或者this语句形式来指定要访问的父类中的构造函数。子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少要有一个构造函数会访问父类中的构造函数(没写也会有默认的)。
封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。
封装的步骤
- 使用 private 关键字来修饰成员变量。
- 对需要访问的成员变量,提供对应的一对 getXxx 方法 、 setXxx 方法。
标准代码——JavaBean
JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的 set 和 get 方法。
public class Student { |
继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
需要注意的是 Java 不支持多继承,但支持多重继承
继承的特性
1、子类拥有父类非 private 的属性、方法
2、子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
3、子类可以用自己的方式实现父类的方法
4、提高了类之间的耦合性
public class Animal{ |
多态
概念:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。
简单的说:就是用基类的引用指向子类的对象。
java引用类型有两个:
编译时类型
编译时类型由声明该变量时使用的类型决定
运行时类型
运行时类型由实际赋给该变量的对象决定
静态多态和动态多态的区别:
多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
多态存在的三个必要条件
- 要有继承
- 要有重写
- 父类引用指向子类对象
class Animal{ |
运行结果:
小狗吃肉 |
上例中main方法中注释了a.sleep(),由于a的编译时类型为Animal,而Animal类中没有sleep方法,因此无法在编译时调用sleep方法。
总的来说:
引用变量在编译阶段只能调用编译时类型所具有的方法,但运行时则执行他运行时类型所具有的方法。
对象类型的转换
父子对象之间的转换分为了向上转型和向下转型,它们区别如下:
- 向上转型 : 通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换
- 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换
向上转型
class A { |
// 结果 |
可以看到打印的是class B的print,这是因为我们通过子类B去实例化的,所以父类A的print方法已经被子类B的print方法覆盖了.从而打印classB的print.
这样做的意义在于:
- 当我们需要多个同父的对象调用某个方法时,通过向上转换后,则可以确定参数的统一.方便程序设计(参考下面示例)
class A { |
// 结果 |
PS:向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.
向下转型
在java中,向下转型则是为了,通过父类强制转换为子类,从而来调用子类独有的方法(向下转型,在工程中很少用到).
为了保证向下转型的顺利完成,在java中提供了一个关键字:instanceof,通过instanceof可以判断某对象是否是某类的实例,如果是则返回true,否则为false,instanceof使用如下:
A a = new B(); //向上转型 (B类是A的子类) |
我们便来分析向下转型的意义.
class A { |
// 结果 |
从上面打印可以看到,我们成功通过向下转型来调用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 class Cat extends Animal{ |
|
抽象类的用处:
虽然也要在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 interface Eat { |
public interface Study { |
public class Cat implements Sleep,Eat{ |
public class Person implements Sleep,Eat,Study{ |
所以也就可以列出抽象类和接口的几点区别:
1.抽象类描述的是“是不是”的问题,而接口描述的是“有没有”的问题;
2.在Java中类的继承是“单继承”,可以“多对一”,但是不允许“一对多”。而一个类却可以同时实现多个接口;
final关键字
final修饰特点
修饰类,类不能被继承
修饰变量,变量就变成了常量,只能被赋值一次
修饰方法,方法不能被重写
内部类
jdk 推出了「内部类」的概念,当然,内部类不仅仅弥补了 Java 不能多继承的一个不足,通过将一个类定义在另一个类的内部,也可以有效的隐藏该类的可见性,等等。
接口 + 内部类 = 多继承
在这之前,Java 的继承机制主要由接口和单根继承实现,通过实现多个接口里的方法,看似能够实现多继承,但是并不总是高效的,因为一旦我们继承了一个接口就必然要实现它内部定义的所有方法。
现在我们可以通过内部类多次继承某个具体类或者接口,省去一些不必要的实现动作。只能说 Java 的内部类完善了它的多继承机制,而不是主要实现,因为内部类终究是一种破坏封装性的设计,除非有很强的把控能力,否则还是越少用越好。
public class Father { |
public class Son { |
显然,我们的 Son 类是不可能同时继承 Father 和 Mother 的,但是我们却可以通过在其内部定义内部类继承了 Father 和 Mother,必要的情况下,我们还能够重写继承而来的各个类的属性或者方法。
这就是典型的一种通过内部类实现多继承的实现方式,但是同时你也会发现,单单从 Son 来外表看,你根本不知道它内部多继承了 Father 和 Mother,从而往往会给我们带来一些错觉。所以你看,内部类并不绝对是一个好东西,它破坏了封装性,用的不好反而会适得其反,让你的程序一团糟,所以谨慎!
当然,并不是贬低它的价值,有些情况下它也能给你一种「四两拨千斤」的感觉,省去很多麻烦。下面我们看看几种不同的内部类类型。
从种类上说,内部类可以分为四类:普通内部类、静态内部类、匿名内部类、局部内部类。
成员内部类
普通内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象
public class InnerClassTest { |
这里的内部类就像外部类声明的一个属性字段一样,因此其的对象时依附于外部类对象而存在的,我们来看一下结果:
我们注意到,内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段。
Java 不允许成员内部类中定义静态的成员。
局部内部类
局部内部类就是在代码块中定义一个类,最典型的应用是在方法中定义一个类。例如:
public class Method { |
匿名内部类
匿名内部类,顾名思义,是没有名字的类,那么既然它没有名字,自然也就无法显式的创建出其实例对象了,所以匿名内部类适合那种只使用一次的情境,例如:
public class Test{ |
这就是一个典型的匿名内部类的使用,它等效于下面的代码:
public class MyObj extends Object{ |
public static void main(String[] args){ |
为了一个只使用一次的类而单独创建一个 .java 文件,是否有些浪费和繁琐?
在我看来,匿名内部类最大的好处就在于能够简化代码块。
显然,我们匿名内部类的构造器会调用相对应的父类构造器进行父类成员的初始化动作。而匿名内部类的本质也就这样,只是你看不到名字而已,其实编译器还是会为它生成单独的一份 Class 文件并拥有唯一的类名的。
其实你从编译器的层面上看,匿名内部类和一个实际的类型相差无几,它也能继承某个类并重写其中方法,实现某个接口的所有方法等。最吸引人的可能就是它无需单独创建类文件的简便性。
静态内部类
静态内部类通过对定义在外部类内部的类加上关键字「static」进行修饰,以标示一个静态内部类,例如:
public class OuterClass { |
首先,MyInnnerClass 作为一个内部类,它可以定义自己的静态属性,静态方法,实例属性,实例方法,和普通类一样。
此外,由于 MyInnerClass 作为一个内部类,它对于外部类 OuterClass 中部分成员也是可见的,但并全部可见,不同类型的内部类可见的外部类成员不尽相同,例如我们的静态内部类对于外部类的以下成员时可见的:
- 静态属性
- 静态方法
所以,我们上述的例子中,外部类 OuterClass 的实例属性 age 对于静态内部类 MyInnerClass 是不可见的。
那么 Java 是如何做到在一个类的内部定义另一个类的呢?
实际上编译器在编译我们的外部类的时候,会扫描其内部是否还存在其他类型的定义,如果有那么会「搜集」这些类的代码,并按照某种特殊名称规则单独编译这些类。正如我们上述的 MyInnerClass 内部类会被单独编译成 OuterClass$MyInnerClass.class 文件。
当然,这里的特殊命名规则其实就是:外部类名 + $ + 内部类名。
如果你想要在外部直接创建一个静态内部类的实例,也是被允许的。例如:
public static void main(String[] args){ |
当然,这样的操作一般也不被推荐,因为一个内部类既然被定义在某个外围类的内部,那它一定是为这个外围类服务的,而你从外部越过外围类而单独创建内部类的实现显然是不符合面向对象设计思想的。
静态内部类的应用场景其实还是很多的,但有一个基本的设计准则是,静态内部类不需要依赖外围类的实例,独立于外围类,为外围类提供服务。
单例设计模式
单例设计模式:保证类在内存中只有一个对象。
如何保证类在内存中只有一个对象呢?
(1)控制类的创建,不让其他类来创建本类的对象。private
(2)在本类中定义一个本类的对象 。Singleton s;
(3)提供公共的访问方式。 public static Singleton getInstance(){return s}
饿汉式的方法
public class Singleton { |
懒汉式的方法
public class Singleton2 { |
工厂方法模式
工厂方法是在设计模式中常用的一种模式,它属于设计模式的创造类型模式,主要用来创建对象。
使用场景
- 创建对象需要大量重复的代码
- 客户端(应用层)不依赖与产品类实例如何被创建、实现细节
- 一个类通过其子类来指定创建那个对象
定义产品接口和工厂接口
/** |
产品实现类
/** |
工厂实现类
/** |
测试类
/** |
结果:
您购买了:海尔冰箱,您需要支付:5999.0 |
参考
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