Big_DATA_006 Java 面向对象三大特性

6、面向对象三大特性

6.1、封装

6.1.1、封装性

封装是一个比较大的概念

把一段功能提取出来,做成一个方法:功能封装

把若干具有相同特征的数据,提取出一个类:类的封装、数据封装

把若干个相关功能的类提取出来:模块封装

6.1.2、属性的封装

如果一个类中的某些属性,不希望外界直接访问,可以将这些属性封装起来。

Q:为什么不希望外界直接访问?

A:因为外界直接访问,可能赋值会出乎我们的逻辑要求。例如:设计一个Person类,有一个属性age,希望这个属性来表示年龄。如果直接让外界访问,外界可以赋值为-10,从语法上讲不错,但是从逻辑上讲,有问题。

关键字 private

private是一个访问权限修饰符,代表私有的。被private修饰的属性、方法,只能在当前的类中访问。

属性的封装

1、使用private,将不希望外界直接访问的属性包装起来,这样外界将不能直接访问。

2、提供相关的访问给外界,用来访问这个属性。

6.1.3、setter、getter方法

可以让外界通过setter方法,对属性进行赋值。通过getter方法,获取属性的值。

这样做的好处:

提供了唯一的用来访问属性的方式,我们就可以在这个方法中添加一些值的过滤。

public class Person {
	private int age;
	
	// 对属性的访问:赋值、读取值
	public void setAge(int age) {
		// 对参数的值做过滤。
		if (age >= 0 && age <= 120) {
			this.age = age;			
		}
	}
	
	public int getAge() {
		return this.age;
	}
}
6.1.4、单例设计模式

设计模式

设计模式,是由前人总结出来的,用来解决特定问题的一种解决方案。

单例

Singleton,解决的问题是需要在一个项目的不同模块中,可以访问到同一个类的同一个对象。

单例类的对象,全局唯一。

饿汉式单例

/**
 * 把这个类设计为单例类,这个类的对象全局唯一,有且只有一个。
 */
public class Chairman {
	// 1、私有化构造方法,杜绝外界通过new的方式来实例化对象的可能性。
	private Chairman() {
		System.out.println("无参构造方法执行了");
	}
	// 2、定义一个静态的、私有的、当前类的对象,作为全局唯一的对象,并实例化。
	private static Chairman instance = new Chairman();
	// 3、提供一个方法,这个方法,可以返回一个全局唯一的当前类的对象。
	public static Chairman getInstance() {
		return instance;
	}
}

懒汉式单例

/**
 * 把这个类设计为单例类,这个类的对象全局唯一,有且只有一个。
 */
public class Chairman {
	// 1、私有化构造方法,杜绝外界通过new的方式来实例化对象的可能性。
	private Chairman() {
		System.out.println("无参构造方法执行了");
	}
	// 2、定义一个静态的、私有的、当前类的对象,作为全局唯一的对象。
	private static Chairman instance = null;
	// 3、提供一个方法,这个方法,可以返回一个全局唯一的当前类的对象。
	public static Chairman getInstance() {
		// 逻辑:
		// 当外界需要一个单例对象的时候,再对单例对象进行判断
		// 如果是null,立即实例化
		if (instance == null) {
			instance = new Chairman();
		}
		return instance;
	}
}

懒汉式单例,比饿汉式单例,在没有使用到单例对象的时候,有一定的空间优势。

懒汉式单例,在多线程的环境中,会有问题。

6.1.5、包 package

包,起到了一个组织文件、组织代码的作用,类似于文件夹。

文件的第一句话

package com.qianfeng.fsingleton;

这句话的作用,是来表示这个文件应该在哪一个包里面。确定文件中的类,编译之后的.class字节码文件所在的路径。

不同的包中的类该如何访问

1、可以通过类的全限定名来访问。

全限定名:从最外层的包开始,一层层的向里查询,一直到这个类。组成了一个完整的查找路径,这个就是类的全限定名。例如:com.qianfeng.gpackage.subpackage1.Person

2、通过导包的形式。

// 导入了这个类,在这个文件中使用这个类的时候,就不用再使用权限定名。
// import com.qianfeng.gpackage.subpackage1.Person;
// import com.qianfeng.gpackage.subpackage1.Dog;

// 导入这个包下的所有的类(但是不包括子包中的类)
import com.qianfeng.gpackage.subpackage1.*;

注意事项:

  • 如果当前包中,和导入的包中有同名的类
  • 如果导入的多个包中有同名的类

以上两种情况下,再使用到这个同名的类的时候,需要使用全限定名。

6.2、继承

6.2.1、什么是继承?

如果多个相关联的类中,有相同的属性和方法,那么可以把相同的部分,单独的提取出来,做成一个类。

这个被提取出来的,具有相同部分的类,称为 – 父类。

被提取出来的,具有相同的属性方法的类,称为 – 子类。

他们之间的关系,就是 – 继承。(子类继承自父类)

父类,又叫做 基类。与之对应的,子类叫做 派生类。

由A类派生出B类:A类是基类,B类是派生类。

在某些书籍中,父类,又被称为 – 超类。(super class)

6.2.2、继承的基本语法

描述继承关系,需要使用到关键字 – extends

class SubClass exetnds SuperClass {
    // 此时,SubClass是子类,SuperClass是父类
    // SubClass 继承自 SuperClass
}
6.2.3、继承的特点

1、Java语言是单继承的语言,一个类只能有一个父类,但是一个类可以有多个子类。

2、一个类在继承了父类的同时,还可以被其他类继承。

3、子类可以访问父类中看得到的成员。

这里所谓的“看得到的”,是由访问权限修饰符来决定的。

4、子类除了拥有父类中的成员之外,还可以定义其他的成员。

6.2.4、继承的使用场景

1、如果多个逻辑上相关联的类之间有相同的成员(属性、方法)。

强调:一定是要在逻辑上关联的类。

2、如果某一个类提供的功能,已经不能满足我们的需求了。需要给这个类拓展功能,可以派生一个子类出来。

因为子类除了拥有父类的所有的成员之外,还可以添加成员。
6.2.5、访问权限修饰符

访问权限:决定了一个类、属性、方法,可以被访问的范围。

访问权限修饰符:使用指定的修饰符,来修饰类、属性、方法,限定这些内容被访问的范围。

访问权限修饰符可以修饰访问范围
公共权限public类、属性、方法在项目的任意位置都可以访问
保护权限protected属性、方法在当前的包中,和跨包的子类中
包权限-类、属性、方法在当前的包中
私有权限private属性、方法在当前的类中

访问权限的大小: public > protected > package > private

6.2.6、继承中的构造方法(重点)

子类对象的实例化

子类对象实例化,在堆上开辟空间。其实,在堆上开辟的空间分为两部分:从父类继承到的属性、自己特有的属性。而这两部分的空间开辟是有先后顺序的,先开辟父类的属性空间,再开辟子类特有的属性空间。在给从父类继承到的属性分配空间的时候(以后就称为实例化父类部分),会默认的调用父类中的无参构造方法来实例化。

如果父类没有无参构造

如果父类没有无参构造,则子类对象的实例化会受到影响,将无法进行实例化。

如何解决

1、给父类添加一个无参构造。

2、在子类的构造方法中,手动调用父类中存在的构造方法。

调用父类的构造方法,使用 super() 来调用。
6.2.7、继承中的方法重写(重点)

Override:子类对从父类继承到的方法进行重新实现。

重写,又叫做 覆写。用子类的实现覆盖掉父类的实现。
重写,其实不单单是重写的父类中的方法,还可以重写接口中的方法。

为什么要重写

  • 父类提供的方法,已经不能满足子类的需求。
  • 对于同样的功能,子类的实现方式和父类不同。

重写的注意事项

1、子类只能重写父类中存在的方法。

2、子类在重写方法的时候,要求方法的名字和参数必须要和父类保持一致。

@Override

用在重写的方法前面。对重写的方法进行验证,验证这个方法是不是重写的方法。

@Override
public void bark() {}

注意事项:

@Override这个注解仅仅起到一个验证的作用。如果一个方法前面不加@Override也可以。

并不是说不加@Override的方法就不是重写的方法。

虽然说这个注解可加可不加,但是习惯上都会加上去。

重写对访问权限、返回值的要求

  • 重写的方法,访问权限要大于等于父类方法的访问权限。
  • 重写的方法,返回值类型要小于等于父类方法的返回值类型。
重写除了对访问权限、返回值类型有要求,还对异常抛出的类型有要求。
6.2.8、Object类

Object是所有类的根类,Java中所有的类都直接或者间接的继承自Object。

toString()

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

返回一个对象的字符串表示形式。将一个对象转成一个字符串表示形式,会自动的调用toString()。

class Person {
	private String name;
	private int age;
	private char gender;
	public Person(String name, int age, char gender) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
	}
	
	@Override
	public String toString() {
		return this.name + ", " + this.age + ", " + this.gender;
	}
}

如果需要定制一个对象转成字符串之后的格式,则需要重写toString()

getClass()

public final native Class<?> getClass();

获取一个对象所对应的类的描述。

hashCode()

public native int hashCode();

返回的当前对象的哈希表中存储的位置索引。

equals()

判断两个对象是否相同。

public boolean equals(Object obj) {
    return (this == obj);
}

如果需要自定义两个对象的比较规则,可以重写equals实现。

equals的重写规范:
1、如果obj是null,一定要返回false;
2、如果this和obj的指向的空间是相同的,一定要返回true;
3、如果this和obj的类型不同,一定要返回false;
4、如果a.equals(b)成立,则b.equals(a)也一定要成立;
5、如果a.equals(b), b.equals(c)都成立,则a.equals(c)也必须要成立;
// 需求:希望如果两个对象的姓名、年龄、性别都是相同的,那么视为是同一个对象
@Override
public boolean equals(Object obj) {
	// 1、判断obj是否是null
	if (obj == null) {
		return false;
	}
	// 2、比较是否是同一块空间
	if (this == obj) {
		return true;
	}
	// 3、判断类型
	if (this.getClass() != obj.getClass()) {
		return false;
	}
	// 4、比较属性值
	Person other = (Person)obj;
	return this.name.equals(other.name) && this.age == other.age && this.gender == other.gender;
}
6.2.9、关键字final

final表示最终,可以修饰类、方法、变量

  • final修饰的类:最终类,无法被继承。
  • final修饰的方法:最终方法,无法被重写。
  • final修饰的变量:最终变量,无法修改值,就是常量。
6.3、多态

6.3.1、对象的转型

对象的转型,其实就是将一个对象转成其他的类型。类似于数据类型转换。

向上转型

  • 由子类类型转型为父类类型。
  • 向上转型一定会成功,不需要任何额外的操作,是一个隐式转换。
  • 向上转型后的对象,将只能够访问父类中存在的成员,子类特有的成员将无法访问。

向下转型

  • 由父类类型转型为子类类型。
  • 向下转型存在失败的可能性,需要额外进行强制操作,是一个显式转换。
  • 向下转型后的对象,将可以访问子类中特有的成员。
6.3.2、instanceof关键字

向下转型可能会失败,如果转型失败,会抛出 ClassCastException 异常。

因为向下转型存在失败的可能性,因此,在向下转型之前,最好先判断一下要转型的对象是不是指定的类型。

判断对象是否是指定的类型,可以使用 instanceof 关键字。

boolean ret = obj instanceof Type;
Dog dog = new Dog();
Animal animal = dog;			// 向上转型
if (animal instanceof Dog) {	// 类型判断
    Dog d = (Dog)animal;		// 向下转型
}
6.3.3、多态的体现

1、父类的引用,可以指向子类的对象。

向上转型后的对象,调用父类中的方法,最终的实现,是子类中的实现。这就是多态。

向上转型后的对象,调用接口中的方法,最终的实现,是实现类中的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值