文章目录
- 第二章、面向对象
- 一、类和对象
- 1、基本概念
- 1)什么是对象
- 2)什么是面向对象
- 3)什么是类
- 4)什么是对象的属性和方法
- 5)类和对象的关系
- 2、单一职责原则
- 3、对象实例化
- 1)Java内存管理之堆和栈
- 4、构造方法
- 5、this关键字
- 6、包
- 1)定义包
- 2)导入包
- 3)常用系统包
- 7、static
- 1)静态属性
- 2)静态方法
- 3)静态代码块
- 8、访问修饰符
- 二、面向对象特性
- 1、封装
- 2、继承
- 1)继承的关系
- 2)实现继承
- 3)方法重写
- 4)方法重写与方法重载
- 5)super
- 6)super pk this
- 7)实例化顺序
- 3、Object 类
- 1)Object 类的常用方法
- 4、final
- 5、注解
- 1)注解的分类
- 2)元注解
- 6. 多态
- 1)向上转型
- 2)绑定机制
- 3)向下转型
- 7、抽象类
- 1)抽象方法
- 8、UML
- 1)泛化
- 2)实现
- 3)依赖
- 4)关联
- 5)聚合
- 6)组合
- 9、接口
- 1)接口和抽象类
- 10、内部类
- 1)成员内部类
- 2)静态内部类
- 3)方法内部类
- 4)匿名内部类
- 11、枚举
- 1)什么是枚举
- 2)为什么使用枚举
- 3)枚举的使用
第二章、面向对象
一、类和对象
1、基本概念
1)什么是对象
万物皆对象、客观存在的事物。
对象:用来描述客观事物的一个实体,由一组属性和方法构成。
2)什么是面向对象
人关注对象
人关注事物信息
3)什么是类
类是模子,确定对象将会拥有的特征(属性)和行为(方法)。
类的特点:
类是对象的类型。
具有相同属性和方法的一组对象的集合。
4)什么是对象的属性和方法
属性:对象具有的各种静态特征。
“有什么”
方法:对象具有的各种动态行为
“能做什么”
5)类和对象的关系
类是抽象的概念,仅仅是模板。
对象是一个你能够看得到、摸得着的具体实体。
类是对象的类型。
对象是特定类型的数据。
具体开发过程中,先定义类再实例化对象。
2、单一职责原则
单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID: SRP 单一责任原则、OCP 开放封闭原则、LSP 里氏替换原则、DIP 依赖倒置原则、ISP 接口分离原则)之一。
它规定一个类应该只有一个发生变化的原因。
所谓职责是指类变化的原因。
如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。
而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。
单一职责原则告诉我们:
一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。
出现单一职责原则原因:
之所以会出现单一职责原则就是因为在软件设计时会出现以下类似场景:T负责两个不同的职责:职责 P1,职责 P2。当由于职责 P1 需求发生改变而需要修改类 T 时,有可能会导致原本运行正常的职责P2 功能发生故障。也就是说职责 P1 和 P2被耦合在了一起。
解决办法:遵守单一职责原则,将不同的职责封装到不同的类或模块中。分别建立两个类 T1、T2,使 T1 完成职责 P1 功能,T2 完成职责 P2 功能。这样,当修改类 T1 时,不会使职责 P2 发生故障风险;同理,当修改 T2 时,也不会使职责 P1发生故障风险。
3、对象实例化
实例化对象的过程可以分为两部分:
- 声明对象 Cat one;
- 实例化对象 new Cat();
每次new对象会产生新的实例化对象。
多个对象可以指向同一块实例化空间。
对象必须被实例化之后才能使用。
对象间的引用传递,实际上传递的是堆内存空间的使用权。
1)Java内存管理之堆和栈
栈:存储局部信息的值,如:方法体中的基本数据类型定义的变量、对象的引用等。当局部作用范围结束时,栈内信息立即自动释放。
当存储内容是由基本数据类型(byte、short、int、long、float、double、char、boolean)声明的局部变量时,在栈中存储的是它们对应的具体数值。
当存储的是局部对象的引用(定义在方法体中的引用类型的变量),存储的是具体对象在堆中的地址。没有时为null。
堆:用来存放动态产生的数据,如:new出来的对象。当对象使用结束,并确定已无实例引用指向堆空间时,JVM才会依据相关垃圾回收机制进行资源回收,完成堆内资源释放。
需要注意:创建出来的对象只包含属于各自的成员变量,并不包括成员方法。(同一个类的方法被不同实例共享)
4、构造方法
1、构造方法与类同名且没有返回值 。
2、构造方法的语句格式 。
public 类名(参数列表){
//初始化代码
}
3、只能在对象实例化的时候调用。
4、当没有指定构造方法时,系统会自动添加无参的构造方法 。
5、当有指定构造方法,无论是有参、无参的构造方法,都不会自动添加无参的构造方法 。
6、一个类中可以有多个构造方法。
5、this关键字
this是Java中常用的关键字,代表当前对象本身。
this的使用:
类方法中调用成员变量,可以用this 关键字,解决成员属性和局部变量同名冲突。
类方法中调用其他成员方法,可以用this关键字,也可以省略this关键字。
调用重载的构造方法:
1、可以在构造方法中,通过this调用本类的另一个构造方法,且必须置于第一行。
2、可以通过this调用本类中带参或无参构造方法,调用带参构造方法时,需要按顺序传入设定的参数。
3、在一个构造方法内只能调用一个构造方法。
4、不能在类中普通成员方法内通过this调用构造方法。
6、包
作用:
1、管理Java文件。
2、解决同名文件冲突。
1)定义包
语法:
package 包名;
例:package com.ql.animal;
注意:
1、必须放在Java源文件中的第一行。
2、一个Java源文件中只能有一个package语句,若缺省则指定为无名包。
3、包名全部英文小写。
4、命名方式:域名倒序.模块.功能,其中用"."来指明包的层次。
基于合理分类,便于管理的思想,约定:
1、推荐将一组互相具有联系并组合起来完成某一功能的类聚集到同一个包中。
2、同一包中,类名不允许重复。
3、不同包中,可以存在同名类。
2)导入包
语法:
import 包名.类名;
例:
导入包中全部类:
import com.ql.*;
导入包中指定类:
import com.ql.animal.Cat;
注意:
1、一个Java文件中可以有多条import语句。
2、java.lang包是默认导入的。
3、import需要写在class语句上面,即一个Java文件中存在顺序是:package - import - class
3)常用系统包
java.lang - - 包含java语言基础的类
java.util - - 包含java语言中各种工具类
java.io - - 包含输入、输出相关功能的类
7、static
static是Java中常用关键字,代表"全局"或者"静态"的意思。
1、static+属性 - - 静态属性
2、static+方法 - - 静态方法
3、static+类 - - 不存在
4、static+方法内局部变量 - - 不存在
1)静态属性
概念:
静态成员:用static修饰的成员变量,通常也称为静态成员、静态属性、类成员、全局属性等。
非静态成员:没有被static修饰的成员变量,也称为叫非静态成员、实例变量、实例成员、对象成员、对象属性等。
特征:
静态成员:
1、静态成员是属于整个类的,由类所进行维护,仅在类初次加载时会被初始化,在类销毁时回收。
2、通过该类实例化的所有对象都共享类中静态资源,任一对象中信息的修订都将影响所有对象。
3、由于静态成员在类加载期间就已经完成初始化,存储在Java Heap 堆(Jdk7.0之前存储在方法区)中静态存储区,因此优先于对象而存在,可以通过类名和对象名两种方式访问。
4、可以将频繁调用的公共信息、期望加快运行效率的成员设置为静态,但是注意,由于其生命周期长,资源占用周期长,要慎用。
非静态成员:
1、非静态成员属于对象独有,每个对象进行实例化时产生各自的成员,随着对象的回收而释放。
2、对象对各自成员信息的修订不影响其他对象。
3、只能通过对象名访问。
2)静态方法
概念:
静态方法:用static修饰的成员方法,通常也称为静态方法、类方法、全局方法。
非静态方法:没被static修饰的成员方法,通常也称为非静态方法、实例方法、对象方法。
特征:
与静态成员相似,静态方法属于整个类的,由类进行维护,优先于对象而存在,因此可以通过类名和对象名两种方式访问,也因此在静态方法中无法直接访问同类中的非静态成员。
3)静态代码块
概念:
普通代码块:定义在方法内部,用{}括起的代码段。
构造代码块:定义在类内部,用{}括起的代码段。
静态代码块:被static修饰的,定义在类内部,用{}括起的代码段。
特征:
普通代码块:
1、可以在方法内出现多次,按顺利在方法调用时执行。
2、可以定义局部变量,作用范围为:自定义位置起,至该代码块结束。
3、方法内定义的局部变量,作用范围为:自定义位置起,至该方法结束,在此期间,不允许在普通代码快中声明同名的局部变量。
构造代码块:
1、可以在类内出现多次,按顺序在每个对象实例化时执行。
2、执行优先级:晚于静态代码块,高于构造方法。
3、每次执行对象实例化时,均会执行一次。
4、可以在构造代码块中直接操作静态和非静态成员。
静态代码块:
1、只能出现在类内,不允许出现在方法内。
2、可以出现多次,按顺序在类加载时执行。
3、无论该类实例化多少对象,只执行一次。
4、不能在静态代码块中直接对非静态成员赋值。
5、不能在静态代码块中声明静态成员,可以声明局部成员。
8、访问修饰符
访问修饰符 | 本类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
二、面向对象特性
面向对象有三大特性:封装、继承、多态。
1、封装
将类的某些信息隐藏在类内部,不允许外部程序直接访问。
通过该类提供的方法来实现对隐藏信息的操作和访问。
特点:
1、只能通过规定的方法访问数据。
2、隐藏类的实例细节,方便修改和实现。
2、继承
一种类与类之间的关系。
使用已存在的类的定义作为基础建立新类。
新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
特点:
1、利于代码复
2、缩短开发周期
1)继承的关系
满足“A is a B ”的关系就可以形成继承关系
如:
猫、狗是动物 =》 猫,狗继承自动物
学生、老师是人 =》 学生,老师继承自人
小轿车、大卡车是汽车 =》 小轿车,大卡车继承自汽车
2)实现继承
使用extends实现封装
//编写父类
class Animal{
//公共的属性和方法
}
//编写子类,继承父类
class Cat extends Animal{
//子类特有的属性和方法
}
class Dog extends Animal{
//子类特有的属性和方法
}
/**
* 注:
* 1、子类只能继承一个父类。
* 2、父类不可以访问子类特有成员。
*/
3)方法重写
语法规则:
在子类中定义,方法名、参数类型、顺序、个数都要与父类继承的方法相同,参数名可以不同。
当子类重写父类方法后,子类对象调用的是重写后的方法。
4)方法重写与方法重载
方法重写:
1、在满足继承关系的子类中。
2、方法名、参数个数、顺序、类型与父类相同。
3、当返回值是void或基本数据类型时,必须相同;当返回值是引用类型时,可以是父类或其子类。
4、访问修饰符的限定范围大于等于父类方法。
5、与方法的参数名无关。
方法重载:
1、在同一个类中。
2、方法名相同。
3、参数个数、顺序、类型不同。
4、返回值类型、访问修饰符任意。
5、与方法的参数名无关。
注:
1、方法重写存在,属性重写不存在的,但是在子类中,可以定义与父类重名的属性。
2、父类构造方法不允许被继承和重写。
5)super
子类访问父类成员
1、访问父类成员方法
super.print();
2、访问父类属性
super.name;
3、访问父类构造方法
super();
子类的构造的过程中必须调用其父类的构造方法:
1、如果子类的构造方法中没有显示调用父类的构造方法,则系统系默认调用父类无参的构造方法。
2、如果子类构造方法中既没有显式调用父类的构造方法,而父类又没有无参的构造方法,则编译出错。
3、使用super调用父类指定构造方法,必须在子类的构造方法的
第一行。
6)super pk this
this: 当前类对象的引用
1、访问当前类的成员方法
2、访问当前类的成员属性
3、访问当前类的构造方法
4、能在静态方法中使用
super: 父类对象的引用
1、访问父类的成员方法
2、访问父类的成员属性
3、访问父类的构造方法
4、能在静态方法中使用
注:
构造方法调用时,super和this不能同时出现。
7)实例化顺序
继承后的初始化顺序:
3、Object 类
Object类是所有类的父类。
一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)。
Java中的每个类都可以使用Object中定义的方法。
1)Object 类的常用方法
equals:
1、继承Object中的equals方法时,比较的是两个引用是否指向同一个对象。
2、子类可以通过重写equals方法的形式,改变比较的内容。(如:String)
toString:
1、输出对象名时,默认会直接调用类中的toString。
2、继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+地址信息。
3、子类可以通过重写toString方法的形式,改变输出的内容以及表现形式。
注:
1、==到底比较的是值还是地址呢?
==比较的是变量里存储的值,不过,对于基本数据类型而言(例如:int、float、double等),变量里存储的是数值,因此,==比较的是数值是否相等;对于引用数据类型,变量里存的是对象的内存地址,因此,==默认比较的是是否指向同一内存空间,也就是地址是否相同。
2、子类中方法可以不经过重写,就可以重载父类的方法吗?
可以,子类中默认继承父类非私有普通成员方法,若无需重写,可以直接重载。
4、final
1、修饰类表示不允许被继承。
//两种写法都可以
public final class Animal{
}
final public class Animal{
}
String、System都是final修饰的类。
2、修饰方法表示不允许被子类重写。
- final修饰的方法可以被继承。
- 不能修饰构造方法。
3、修饰变量表示不允许修改。
- 方法内部的局部变量 》在使用之前被初始化赋值即可。
- 类中成员变量 》 只能在定义时或者构造代码块、构造方法中进行初始化设置。
- 基本数据类型的变量 》 初始赋值之后不能更改。
- 引用类型的变量 》 初始化之后不能再指向另一个对象,但指向的对象的内容是可变的。
4、可配合static使用,如定义全局常量、配置信息等。
5、使用final修饰可以提高性能,但会降低可扩展性。
注:
被static修饰的静态方法也是不能被重写的,但是子类中可以包含和父类同名的静态方法。
5、注解
JDK1.5版本引入的一个特性。
可以声明在包、类、属性、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释。
1)注解的分类
按照运行机制分:
1、源码注解:注解只在源码阶段保留,在编译阶段被丢弃,如@Override。
2、编译时注解:注解会在编译时期保留,在加载class文件时会被丢弃,如Spring注解@NotNull。
3、运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解,如Spring注解@Autowired。
按照源来分:
1、来之JDK的注解。
2、来自第三方的注解。
3、我们自己定义的注解。
2)元注解
主要用来定义注解的注解,如@Target。
6. 多态
多态(Polymorphism)按字面的意思就是“多种状态”,是面向对象的程序设计语言最核心的特征。
从一定角度来看,封装和继承几乎都是为多态而准备的。
多态分类
1、编译时多态(设计时多态):方法重载。
2、运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态。
我们平时说得多态,多指运行时多态。
一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
必要条件:
- 满足继承关系
- 父类引用指向子类对象
1)向上转型
又称为自动转型、隐式转型。既是父类引用指向子类实例,也表示子类的对象可以赋值给父类的对象。向上转型是安全的,因为任何子类都继承并接受了父类的非私有成员方法。
当一个子类对象向上转型父类类型以后,就被当成了父类的对象,所能调用的方法会减少,只能调用子类重写了父类的方法以及父类派生的方法,而不能调用子类独有的方法。
2)绑定机制
绑定就是将一个方法调用同一个方法所在的类连接到一起。Java种存在静态绑定和动态绑定两种。
其中,静态绑定是指程序运行之前进行绑定(由编译器和链接程序完成的),也叫做前期绑定。
动态绑定则是指在程序运行期间由JVM根据对象的类型自动的判断应该调用哪个方法,也叫做后期绑定。
多态的实现可以借由向上转型和动态绑定机制来完成,其中,向上转型实现了将子类对象向上转型为父类类型;而动态绑定机制能识别出对象转型前的类型,从而自动调用该类的方法,两者相辅相成。
3)向下转型
向下类型转换(Downcast):将父类型转换为子类型。
- 将一个指向子类对象的父类引用赋值给一个子类的引用。
- 强制类型转换,是大类型到小类型。
- 父类型的引用必须指向转型的子类的对象,即指向谁才能转换成谁,不然也会编译出错。
通过instanceof运算符,来解决引用对象的类型,避免类型转换的安全性问题,提高代码的强壮性。
if(one instanceof Cat){
Cat temp = (Cat)one;
}
注意:
1、父类引用指向子类实例时,可以调用子类重写父类的方法以及直接继承父类的方法,无法调用子类特有的方法。
2、静态static方法属于特殊情况,静态方法只能继承,不能重写。调用的时候用谁的引用,则调用谁的版本。
7、抽象类
在面向对象的概念中,所有对象都是通过类来描述的,但并不是所有的类都是用来描述对象的。当一个类中没有包含足够的信息以描述一个具体的对象时,这样的类就是抽象类。
定义抽象类的意义在于:
- 为其子类提供一个公共的类型(父类引用指向子类对象)。
- 封装子类冲的重复内容(成员变量和方法)。
- 将父类设计成抽象类后,既可借由父子继承关系限制子类的设计随意性,在一定程度上避免了无意义父类的实例化。
public abstract class Animal{
//成员变量和方法
}
应用场景:
某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。
1)抽象方法
- abstract也可用于方法——抽象方法
- 抽象方法没有方法体
- 抽象方法必须在抽象类里
- 抽象方法必须在子类中被实现,除非子类也是抽象类
public abstract void eat();
抽象类 & 抽象方法
使用规则
- abstract定义抽象类。
- 抽象类不能直接实例化,只能被继承,可以通过向上转型完成对象实例。
- abstract定义抽象方法,不需要具体实现。
- 包含抽象方法的类是抽象类。
- 抽象类中可以没有抽象方法。
- 子类如果没有重写父类所有的抽象方法,则也要定义为抽象类。
- abstract 不能与static、final、private共存。
- 抽象方法在子类实现时访问权限必须大于等于父类方法。
8、UML
UML(Unified Modeling Language),又称统一建模语言或标准建模语言。它是一种支持模型化和软件系统开发的图形化语言。
它可以为软件开发的所有阶段提供模型化和可视化支持,可用于多种类型软件系统开发建模的各个阶段。通常我们会结合UML图例来表示类与类之间的各种关系。
类的层次结构代表类与类之间的关系,类的关系有很多种,包括泛化、实现、依赖、关联、聚合和组合。这些关系在大的类别上还可分为两种:横向关系和纵向关系,其中,纵向关系就是泛化、实现,而横向关系就是剩下的4种。它们的强弱关系:依赖 < 关联 < 聚合 < 组合。理解这些类的关系,并应用与类的层次结构设计中,有助于我们更好的进行代码组织结构优化。
纵向关系:
1)泛化
泛化关系就是继承,表示一般与特殊的关系,即“一般”元素是“特殊”元素的泛化。在继承中,“一般类”就是父类,“特殊类”就是子类。它指定了子类如何特化父类的所有特征和行为。它们之间是“is a”的关系。也就是说如果两个类之间满足“A is a B”的关系,则A就是B的子类。
在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类。譬如:猫类继承自动物类,即:猫是动物的一种,既有猫的特性也有动物的共性的关系。此时,实线由猫类出发,空心箭头指向动物类。
2)实现
实现和泛化关系相似,逻辑上也通过“is a ”表示。区别在于,实现关系通常是继承自一个抽象类(abstract)或接口(interface),表示类是接口或者抽象类所有特征和行为的实现,而泛化关系通常继承一个具体类。
在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从子类指向抽象父类或接口。譬如:鹦鹉具有飞翔的能力,自然满足“鹦鹉 is a 能飞”即:鹦鹉是能飞的这样的关系。此时,虚线由鹦鹉类出发,空心箭头指向飞翔。
横向关系:
3)依赖
依赖就是一个类A使用到了另一个类B,即一个类的实现需要另一个类的协助。而这种使用关系是具有临时性的、相对弱的。它们之间是“use a ”的关系。通常,当我们在类A中,把类B的对象作为方法参数时;方法中使用到了类B对象作为局部变量时,体现的就是两者之间的依赖关系。建议:尽量避免双向依赖。
在UML类图设计中,依赖用一条带箭头的虚线表示,从A类指向依赖的B类。譬如:某人要过河,需要借用一条船,此时,人与船之间的关系就是依赖。即在人类的过河方式中,需要依赖船的对象作为方法的输入参数。此时,箭头由人类出发,指向船。
4)关联
关联体现的是两个类之间一种强依赖关系,这种关系比依赖更强,一般是长期性的,一种拥有的关系,它使一个类知道另一个类的属性和方法。他们之间能用“has a ”表示。通常,当我们在类A中,把类B的对象作为类中成员时,体现的就是两者之间的关联关系。
关联可以是单向、双向的。在UML类图设计中,双向关联,是使用一条实线或一条实线+两个普通箭头(或实心三角形箭头)表示;单向关联,则是用一条实线+一个普通箭头(或实心三角形箭头)表示,无论是单向还是双向,箭头均指向被拥有者。
譬如:老师和学生是双向关联,一名老师会管理多名学生,一名学生也会有多名授课老师;学生跟学号之间是单向关联,一个学生只能对应一个唯一的学号。
5)聚合
聚合关系是一种强的关联关系,它体现的是整体与部分的关系,且部分可以离开整体而单独存在,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。它们之间也是“has a ”的关系。关联和聚合在语法上无法区分,必须结合具体的逻辑场景。
在UML类图设计中,聚合关系以空心菱形加实线表示,菱形指向整体。譬如,学校有很多老师,老师离开学校还可以正常存在。
6)组合
组合也是整体与部分的关系,但是比聚合关系还要强的关系。此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,既要求普通的聚合关系中代表整体的对象,负责代表部分的对象的生命周期。它体现的是一种“contains a ”的关系。
在UML类图设计中,组合关系以实心菱形加实线表示,菱形指向整体。譬如:公司和部门是整体和部分的关系,没有公司就不存在部门。
9、接口
接口定义了某一批类所需要遵守的规范。
接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某些方法。
语法:
[修饰符] interface 接口名 [extends 父接口 1,父接口2…]
{
零个到多个常量定义…
零个到多个抽象方法的定义…
零个到多个默认方法的定义…(jdk1.8新增)
零个到多个静态方法方法的定义…(jdk1.8新增)
}
接口的语法规则
- 接口及接口成员默认的访问权限为:public 或 默认。
- 常量默认添加 public static final 关键字(可省略这三个任意关键字)。
- 抽象方法默认添加 public abstract 关键字(可省略这两个个任意关键字)。
- 只有default方法及static方法可以添加方法体(jdk1.8之后);
默认方法可以在实现类中重写,并可以通过接口的引用调用;
//实现类中调用默认方法
IPerson.super.eat();
静态方法不可以被重写,可以同接口名调用。
5. 实现接口的类如果不能实现所有接口中待重写的方法,则必须设置为抽象类。
6. 接口可以实现多继承,即一个子接口可以同时继承多个父接口。
7. 一个类可以继承自一个父类,同时实现多个接口。
8. 当一个类同时实现多接口,且其中同时具有相同方法时,实现类需重写该方法,否则会编译报错。
注:
1、一个Java文件中可以存在多个类或接口,但只能存在一个public,并且修饰的类或接口名和文件名同名。
2、类实现多接口,若多接口中存在相同成员,类中不重新实现的话无法判断调用哪个。
3、父类实现多接口,若父类和多接口中存在相同成员,子类中不重新实现的话无法判断调用哪个常量,优先调用父类方法。
1)接口和抽象类
抽象类
1、抽象类不能实例化、只能通过引用指向子类实例。
2、含有抽象方法的类一定是抽象类,但抽象类中可以没有抽象方法。
3、如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则也需要设置为抽象类。
4、子类只能通过extends继承一个抽象父类。
5、抽象类中静态成员和方法可以被子类继承应用。
6、类中抽象方法必须加 abstract 关键字。
7、抽象方法支持 public 、protected 和默认访问权限。
接口
1、接口不能实例化,只能通过引用指向实现类实例。
2、如果一个类未实现接口的所有抽象方法,则该类需要设置成抽象类,反之则不必。
3、子接口可以通过 extends 继承多个接口,接口之间通过逗号分隔。
4、实现类可以通过 implement 实现多个接口,通过逗号分隔,当实现类同时继承父类并实现接口时,需要先继承后实现。
5、默认方法和静态方法自jdk1.8后可以在接口中应用,默认方法可以再实现类中应用,静态方法只属于接口。
6、接口中的抽象方法可以不写 public abstract 修饰符,且只能是 public 修饰。
7、接口中常量可以不写 public static final 修饰符。
10、内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类,与之对应,包含内部类的类被称为外部类。
内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类,更好的实现了信息隐藏。
1)成员内部类
内部类中最常见的就是成员内部类,也称为普通内部类。
//外部类
public class Person {
public static int age;
public Object getHeart() {
return new Heart();
}
// 成员内部类
public class Heart{
int age = 13;
public String beat() {
return Person.this.age + "岁的心脏在跳动";
}
}
}
获取成员内部类对象实例,方式1:new 外部类.new 内部类
Person.Heart myHeart=new Person().new Heart();
System.out.println(myHeart.beat());
获取成员内部类对象实例,方式2:外部类对象.new 内部类
Person lili=new Person();
myHeart=lili.new Heart();
System.out.println(myHeart.beat());
获取成员内部类对象实例,方式3:外部类对象.获取方法
myHeart=lili.getHeart();
System.out.println(myHeart.beat());
1、内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化。
2、内部类相当于外部类的一个成员变量,可以使用任意访问修饰符。
3、内部类中定义的方法可以直接访问外部类中的数据,而不受访问控制符的影响。
4、外部类不能直接使用内部类的成员和方法,需要借由内部类对象完成。
5、如果外部类和内部类具有相同的成员,内部类默认优先访问自己的成员;可以通过“外部类.this.对象成员” 以及“外部类.静态成员” 的方式访问外部类成员。
6、内部类编译后产生:外部类$内部类.class
2)静态内部类
static修饰的内部类。
静态内部类对象可以不依赖于外部类对象,直接创建。
//外部类
public class Person {
public static int age;
public Object getHeart() {
return new Heart();
}
// 静态内部类
public static class Heart {
public static int age = 13;
int temp = 22;
public static void say() {
System.out.println("hello");
}
public void eat() {
}
public String beat() {
new Person().eat();
return Person.age + "岁的心脏在跳动";
}
}
}
获取静态内部类对象实例
Person.Heart myHeart=new Person.Heart();
System.out.println(myHeart.beat());
Person.Heart.say();
1、静态内部类中,只能直接访问外部类的静态成员,如果需要调用非静态成员,可以通过对象实例。
2、静态内部类对象实例时,可以不依赖于外部类对象。
3、可以通过外部类.内部类.静态成员的方式,访问内部类中的静态成员。
4、当内部类属性与外部类属性同名时,默认直接调用内部类中的成员;
如果需要访问外部类中的静态属性,则可以通过 外部类.属性 的方式;
如果需要访问外部类中的非静态属性,则可以通过 new 外部类().属性 的方式。
3)方法内部类
定义在外部类方法中的内部类,也称局部内部类。
//外部类
public class Person {
public static int age;
public void eat() {
System.out.println("人会吃东西");
}
// 方法内部类
public Object getHeart() {
class Heart {
public final int age = 13;
int temp = 22;
public final void say() {
System.out.println("hello");
}
public void eat() {
}
public String beat() {
new Person().eat();
return Person.age + "岁的心脏在跳动";
}
}
return new Heart().beat();
// new Heart().temp=12;
temp=33;
// return new Heart();
}
}
1、方法内部类只在其定义所在的方法的内部可见,即只在该方法内可以使用。
2、方法内部类不能使用访问控制符和 static 修饰符,但可以使用final和abstract修饰。
3、编译后产生:外部类$数字内部类.class
4)匿名内部类
如果某个类的实例只是用一次,则可以将类的定义与类的创建,放到一起完成,或者说在定义类的同时就创建一个类。以这种方法定义的没有名字的类称为匿名内部类。
public abstract class Person {
private String name;
public Person(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void read();
}
public class PersonTest {
public void getRead(Person person){
person.read();
}
public static void main(String[] args) {
PersonTest test=new PersonTest();
//匿名内部类
test.getRead(new Person(){
{
//构造代码块
}
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("男生喜欢看科幻类书籍");
}
});
test.getRead(new Person(){
@Override
public void read() {
System.out.println("女生喜欢读言情小说");
}
});
}
}
适用场景:
- 只用到类的一个实例
- 类在定义后马上用到
- 给类命名并不会导致代码更容易被理解
使用原则:
1、不能有构造方法,可以通过构造代码块实现数据初始化。
2、 不能定义任何静态成员、静态方法。
3、不能使用public、protected、private、static、abstract、 final修饰。
4、因匿名内部类也是局部内部类,所以局部内部类的所有限制都对其生效。
5、一个匿名内部类一定是在new的后面,用其隐含实现一个接口或继承一个类,但是两者不可兼得。
6、只能创建匿名内部类的一个实例。
7、匿名内部类在编译的时候由系统自动起名为Outter$1.class
8、 一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
9、通过匿名内部类返回的是一个对象的引用,所以可以直接使用或将其复制给一个对象变量。
11、枚举
1)什么是枚举
枚举可以看成是一些常量值的集合,以星期为例,
enum Week{MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}
其中,enum 是定义枚举的关键字,Week是定义的枚举类型,大括号中的内容是具体的枚举值。使用的时候Week.MONDAY表示星期一。
2)为什么使用枚举
1、减少代码中的bug
2、增加代码的可读性
3)枚举的使用
public class WeekDemo{
//定义星期的枚举
enum Week{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}//注意此处没有分号的
}
//sout(Week.TUESDAY)打印出TUESDAY
枚举可以定义在类的内部,也可以声明在独立的类中,但不能定义在方法中。
枚举值不一定要大写。
枚举可以结合if和switch语句使用。