Java中面向对象详解
一.定义
面向对象是:将事务高度抽象化的编程模式
将问题分解成一个个小步骤,对每个步骤进行抽象,形成对象,通过不同的对象之间调用,组合解决问题。
在进行面向对象进行编程时,要把属性、行为等封装成对象,然后基于这些对象及对象的能力进行业务逻辑的实现。创建一次,重复使用
二.面向对象三个特性
2.1 封装
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装是面向对象的特征之一,是对象和类概念的主要特性。简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
封装实现的三步骤:
1. 将属性进行私有化private
2. 提供一个公共的set方法,用于堆属性判断并赋值
public void setXxx(类型 参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性 = 参数名;
}
3. 提供一个公共的get方法,用于获取属性的值
public void getXxx(){
return xx;
}
2.1.1 访问修饰符
修饰符 | 当前类 | 同一包内 | 子类 | 其他包 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |
2.1.2 this关键字
this关键字指的是当前类
注意细节:
a. this 关键字可以用来访问本类的属性、方法、构造器
b. this 用于区分当前类的属性和局部变量
c. 访问成员方法的语法:this.方法名(参数列表)
d. 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一条语句)
e. this 不能在类定义的外部使用,只能在类定义的方法中使用
2.1.3 内部类
内部类就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。
分类:
定义在外部类局部位置(比如方法内):
1. 局部内部类(有类名)
2. 匿名内部类(没有类名)
定义在外部类成员位置(比如方法内):
1. 成员内部类(没有static修饰)
2. 静态内部类(使用static修饰)
主要使用(非static关键字修饰的内部类):
1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据。
3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
4. 外部类访问内部类成员方式:创建对象再访问,但需在作用域内实现
5. 如果外部类和局部类的成员重名时,遵循就近原则,如果想访问外部类的成员,可以使用this关键字去访问
2.2 继承
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。
继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。
2.2.1 方法的重写
方法重写就是子类的一个方法和父类一个方法名称、返回类型、参数一样,子类的方法覆盖了父类的方法,那我们就说子类 重写了父类的方法。
重写条件:
- 参数列表必须完全与被重写方法的相同;
- 返回类型必须完全与被重写方法的返回类型相同;
- 访问级别的限制性一定不能比被重写方法的强;
- 访问级别的限制性可以比被重写方法的弱;
注意细节:
a. 子类的方法的形参列表、方法列表,要和父类方法的形参列表,方法名称完全一样
b. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的父类
c. 子类方法不能缩小父类方法的访问权限
d. 重写方法一定不能抛出新的检查异常或比被重写的方法声明的检查异常更广泛的检查异常。
e. 重写的方法能够抛出更少或更有限的异常(也就是说,被重写的方法声明了异常,但重写的方法可以什么也不声明)。
f. 不能重写被标示为 final 的方法。
g. 如果不能继承一个方法,则不能重写这个方法。
2.2.2 方法的重载
Java同一个类中,多个同名方法的存在,但要求参数类型或者个数顺序不一致任意,互相称为重载函数或方法重载。
重载条件:
- 被重载的方法必须改变参数列表;
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载
2.2.3 继承的初始化顺序
父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化--->子类对象构造方法
2.2.4 final关键字
final可以修饰类、属性、方法和局部变量
使用情景:
- 当不希望类被继承,可以用final修饰
- 当不希望父类的某个方法被子类重载/重写时,可以用final关键字修饰
- 当不希望类的某个属性的值被修改,可以用final修饰
- 当不希望某个局部变量被修改,可以使用final修饰
注意细节:
- final修饰的属性又叫常量,一般用XX_XX_XX 来命名
- final修饰的属性再定义时,必须赋初始值,并且以后不能修改,赋值可以在以下位置任选:
定义时:如public final int MAX_sum=8;
在构造器中
在代码块中 - 如果final修饰的属性是静态的,则初始化的位置只能是
定义时或在静态代码块,不能在构造器中赋值 - final不能继承,但是可以实例化对象
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
- 一般来说,如果一个类是final类,就没有必要再将方法修饰成final方法
- final不能修饰构造器
- final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
2.2.5 super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器
基本语法:
- 访问父类的属性,但不能访问父类的private属性。例:super.属性名
- 访问父类的方法,不能访问父类的private方法。例:super.方法名(参数列表)
- 访问父类的构造器:super(参数列表),只能放在构造器的第一句,只能出现一句
细节:
- 调用父类的构造器分工明确。父类属性由父类初始化,子类属性子类初始化
- 当子类中有和父类的成员重名时,为了访问父类的成员,必须使用super.如果没有重名,使用super、this、直接访问是一样的效果
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员,如果多个基类都有同名的成员,使用
super访问遵循就近原则。 - 如果查询到父类super成员是private私有的,爷爷类super是公共的,也会查询父类的对象,直接报错,而不会看爷爷类的成员
- 子类的构造器默认有一个隐藏的super无参构造器
2.3 多态
所谓多态就是指一个类实例的相同操作在不同对象有不同解释,产生不同的执行结果。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
最常见的多态就是将子类传入父类参数中,运行时调用父类方法时通过传入的子类决定具体的内部结构或行为
多态的概念比较简单,就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
如果按照这个概念来定义的话,那么多态应该是一种运行期的状态
多态的必要条件
为了实现运行期的多态,或者说是动态绑定,需要满足三个条件。即有类继承或者接口实现、子类要重写父类的方法、父类的引用指向子类的对象
2.3.1 抽象类,接口
详细请看这一篇文章
2.3.2 接口和匿名内部类使用
public class test_1 {
public static void main(String[] args) {
System.out.println("欢迎来到 宠物商店");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你要领养的宠物的名字");
String next = scanner.next();
System.out.println("请输入你要领养的宠物类型:1.狗狗\t2.企鹅\t3.猫");
int i = scanner.nextInt();
Pet pet = new Pet(next,100,0);
switch (i){
case 1:
System.out.println("请选择品种:1.拉布拉多\t2.肯达尔");
int next1 = scanner.nextInt();
String next2= (next1 == 1 ? "拉布拉多" : "肯达尔");
System.out.println(pet);
pet.show(new IA() {
@Override
public void aic() {
System.out.println("品种是"+next2);
}
@Override
public void aii() {
System.out.println(next2+"吃饱了,健康值增加5");
pet.setHealth(-5);
}
public void play() {
pet.setHealth(5);
pet.setLove(5);
System.out.println(next2+"正在乱跑");
}
});
break;
case 2:
System.out.println("请选择性别:1.Q仔\t2.Q崽");
int next3 = scanner.nextInt();
String next4= (next3 == 1 ? "Q仔" : "Q崽");
System.out.println(pet);
pet.show(new IA() {
@Override
public void aic() {
System.out.println("名字是是"+next);
}
public void aii() {
System.out.println(next4+"吃饱了,健康值增加3");
pet.setHealth(-3);
}
@Override
public void play() {
pet.setHealth(5);
pet.setLove(5);
System.out.println(next4+"正在闲逛");
}
});
break;
case 3:
System.out.println("请选择性别:1.C仔\t2.C崽");
int next5 = scanner.nextInt();
String next6= (next5 == 1 ? "Q仔" : "Q崽");
System.out.println(pet);
pet.show(new IA() {
@Override
public void aic() {
System.out.println("名字是是"+next6);
}
public void aii() {
System.out.println(next6+"吃饱了,健康值增加3");
pet.setHealth(-3);
}
@Override
public void play() {
pet.setHealth(5);
pet.setLove(5);
System.out.println(next+"正在舔毛");
}
});
break;
default:
System.out.println("对不起没有这种类型宠物");
}
}
}
public class Pet {
private final String name;
int health;
public void setHealth(int health) {
this.health -=health;
}
public void setLove(int love) {
this.love += love;
}
private int love;
public Pet(String name, int health, int love) {
this.name = name;
this.health = health;
this.love = love;
}
@Override
public String toString() {
return
"name='" + name + '\'' +
", health=" + health +
", love=" + love ;
}
public void show(IA ia){
ia.aic();
ia.aii();
ia.play();
}
}
interface IA extends IC{
public void aic();
public void play();
}
interface IC{
public void aii();
}