Java封装与继承
1、垃圾回收机制(Garbage Collection)
JAVA的内存管理实际上指的就是对象的管理,其中包括对象空间的分配和释放。
对象空间的分配:
使用new关键字创建对象即可
对象空间的释放:
将引用数据类型变量赋值null即可。垃圾回收器将负责回收所有“不可达”对象的内存空间。
System.gc() 通知的作用,通知垃圾回收机制回收垃圾,是否回收,什么时候回收无法控制
finalize() 回收垃圾会先调用对象的这个方法
2、package包和导包
在java中我们通过package实现对类的管理,我们将相同功能的类放到一个包中,并且日常项目的分工也是以包作为边界。
注意:同一个包中,不允许有相同的类名。
【示例】包的命名举例
com.object
com.object.demo
注意:com.object和com.object.demo,这两个包没有包含关系,是两个完全独立的包。只是逻辑上看起来后者是前者的一部分。
【示例】包的声明举例
package com.object; // 包的声明,必须在有效代码的第一行(注释不算)
// 以下是业务逻辑所需要的代码
class Person {}
注意:当前包中的类可以直接使用!
2.1 java常用包
常用包 | 说明 |
---|---|
java.lang | 包含一些Java语言的核心类,如String、Math、System等。 |
java.awt | 包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。 |
java.net | 包含执行与网络相关的操作的类。 |
java.io | 包含能提供多种输入/输出功能的类。 |
java.util | 包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。 |
2.2import导入类
导包的格式:import 包名.类名;
import 导包代码书写的位置:在声明包 package 后,定义所有类 class 前。
【示例】import的使用举例(一)
package com.object;
// 导入位置:在声明包package后,定义所有类class前。
import java.util.Date; // 导入日期类
public class Test {
public static void main(String[] args) {
// 使用Date类
Date date = new Date();
}
}
【示例】import的使用举例(二)
package com.object;
// 导入位置:在声明包 package后,定义所有类 class前。
import java.util.*; // 导入该包下所有的类。会降低编译速度,但不会降低运行速度。
class Test {
public static void main(String[] args) {
// 使用java.util包下的类
Date date = new Date();
Scanner s = new Scanner(System.in);
}
}
注意:如果导入两个同名的类,只能用包名+类名来显示调用相关类。
3、封装(encapsulation)
面向对象编程有三大特性:封装、继承、多态。
封装是指隐藏对象的属性和实现细节,仅对外提供公共的访问方式。
封装的优点
- 提高代码的安全性。
- 提高代码的复用性。
封装原则:
- 将不需要对外提供的内容都隐藏起来。
- 把属性都隐藏,提供公共方法对其访问。
3.1访问控制符
修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
---|---|---|---|---|
private | * | |||
default | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
private:表示私有的,作用范围是当前类(类可见性)。
default:表示没有修饰符修饰,作用范围是当前类+当前包(包可见型)。
protected:表示受保护的,作用范围是当前类+当前包+其它包中的子类(子类可见性)。
public:表示公开的,作用范围是是当前类+当前包+其它包(项目可见性)。
注意事项:
- 类中的属性和方法访问权限共有四种:private、default、protected和public。
- 类的访问权限只有两种:public和default。
- 访问权限控制符不能修饰局部变量。
3.2成员变量封装
我们在操作属性时,通常会对数据进行封装,这样就可以增加了数据访问限制,增加了程序可维护性。
实现方式:提供相应的get/set方法来访问相关属性,使用set方法实现对属性的赋值操作,使用get方法实现对属性的取值操作。
关于使用权限修饰符补充:
- 属性一般使用private访问权限。
- 提供访问相关属性get/set方法通常是public修饰的,以方便对属性的赋值与读取操作。
- 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
【示例】JavaBean的封装实例
class Person {
// 成员方法,一般使用private修饰
private String name;
private int age;
private boolean marry; // 是否已婚
// set和get方法,一般使用public修饰
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// boolean类型的成员变量,get方法是is开头的。
public boolean isMarry () {
return marry;
}
public void setMarry(boolean marry) {
this.marry = marry;
}
}
public class EncapsulationDemo {
public static void main(String[] args) {
Person person = new Person();
// 赋值操作
// person.name = "小明"; // 编译失败
person.setName("小明");
person.setAge(18);
person.setMarry(true);
// 取值操作
// String name = person.name; // 编译失败
String name = person.getName();
int age = person.getAge();
boolean isMarry = person.isMarry);
System.out.println("name:"+name+" age:"+age+" isMarry:"+isMarry);
}
}
JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,并提供set和get方法操作成员成员变量。
【示例】可见性封装的使用案例
class Person {
// 成员方法
private String name;
private int age;
// 构造方法
public Person() {}
public Person(String name, int age) {
this.name = name;
// 调用set方法来给年龄赋值
setAge(age);
}
// set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 新增需求:获取年龄的时候要求返回虚岁
public int getAge() {
return age + 1;
}
// 设置年龄的时候是实岁
public void setAge(int age) {
// 在赋值之前先判断年龄是否合法
if (age > 130 || age < 0) {
this.age = 0; // 不合法赋默认值0
} else {
this.age = age; // 合法才能赋值给属性
}
}
// 成员方法
void show() {
// 输出:name: 小明 age: 0,思考:为什么会这样???
System.out.println("name: " + name + " age: " + age);
}
}
public class EncapsulationDemo {
public static void main(String[] args) {
Person person = new Person();
// 赋值操作
person.setName("小明");
person.setAge(150);
// 取值操作
String name = person.getName();
int age = person.getAge();
// 调用成员方法
person.show();
// 输出:name:小明 age:1,思考:为什么会这样???
System.out.println("name:" + name + " age:" + age);
}
}
4、继承(inheritance)
继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
在Java之中,如果要实现继承的关系,可以使用如下的语法:
class 父类 {
}
class 子类 extends 父类 {
}
子类又被称为派生类,父类又被称为超类(Super Class)。
【示例】使用extends实现继承
// 父类:Person类
class Person {
// 成员变量
private String name;
// set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 成员方法
public void eat() {
System.out.println("eat...");
}
}
// 子类,Teacher类
public class Teacher extends Person {
// 成员变量
private String title; // 职称
// set和get方法
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
// 成员方法
public void teaching() {
System.out.println("授课...");
}
}
// 测试类
public class InheritanceDemo {
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("雨梦"); // 父类属性
t.setTitle("JavaSE"); // 子类属性
t.eat(); // 父类方法
t.teaching(); // 子类方法
System.out.println("name:"+t.getName()+" age:"+t.getAge());
}
}
4.1继承的使用要点
继承的好处
- 继承的出现提高了代码的复用性,提高软件开发效率。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
继承的注意点
- java 只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类。
- java 支持多层(重)继承,即一个类的父类可以再去继承另外的父类(继承体系)。
- 如果定义一个类时,没有使用extends,则它的父类默认是:java.lang.Object。
5、方法的重写(override)
当父类的方法不能满足子类的需求时,我们可以在子类中重写父类的方法,重写也称为复写。
方法重写需要符合以下三个要点:
-
== :方法名、形参列表必须相同。
-
= :访问权限,子类大于等于父类。
父类的私有方法不能被重写,static修饰的方法不能被重写。
-
<= :返回值类型和异常类型,子类小于等于父类。
【示例】方法重写案例
// 父类
class Parent {
public void show(){
System.out.println("父类中的 show方法执行");
}
}
// 子类
class Child extends Parent {
// 子类重写父类的show方法
public void show(){
System.out.println("子类中的 show方法执行");
}
}
public class ExtendsMethodDemo {
public static void main(String[] args) {
Child p = new Child();
p.show(); // 执行的是子类的show方法
}
}
注意:如果子类重写了父类的方法,通过子类对象调用该方法时,调用的方法就是被覆写过的方法!
如果子类方法需要使用父类方法的功能,可使用super关键字来调用父类方法,该关键字引用了当前对象的父类对象。这样,即沿袭了父类的功能,又定义了子类特有的功能 (super关键字的使用和this非常相似)。
6、super关键字
super可以理解为直接父类对象的引用,或者说super指向子类对象的父类对象存储空间。我们可以通过super来访问父类中被子类覆盖的方法或属性,super的使用和this关键字非常的相似。
【示例】super关键字使用
class Parent {
String name = "父类";
public void study() {
System.out.println("父类中的study方法");
}
}
class Child extends Parent {
public void show() {
// 调用父类的成员方法
super.study();
// 调用父类的成员变量
System.out.println("name:" + super.name);
}
}
public class SuperDemo extends Parent {
public static void main(String[] args) {
new Child().show();
}
}
6.1super调用父类构造方法
子类继承父类,子类的构造方法必须调用父类的构造方法,如果子类的构造方法中没有显示的调用父类的构造方法,则系统默认调用父类的无参数构造方法。
【示例】默认调用父类的无参数构造方法
class Parent {
public Parent() {
System.out.println("parent 构造方法");
}
}
class Child extends Parent {
public Child() {
// 如我们没有显示的使用super(),那么会默认调用父类的无参构造方法
System.out.println("Child 构造方法");
}
}
public class SuperDemo {
public static void main(String[] args) {
// 创建子类对象,父子类构造方法都执行!!!!
new Child();
}
}
若想指定调用父类中的有参构造函数,那么可在supper()中添加参数,指定调用父类中的某个构造函数,而且必须放在构造方法的第一条语句
【示例】指定调用父类构造方法
class Parent {
String name;
public Parent() {} // 无参构造方法建议加上!!!
public Parent(String name) {
this.name = name;
System.out.println("parent 构造方法");
}
}
class Child extends Parent {
int age;
public Child(String name, int age) {
// super(形参列表) 必须放在构造方法的第一行
super(name);
this.age = age;
System.out.println("Child 构造方法");
}
void show() {
// 输出:name:小明 age:18
System.out.println("name:" + name + " age:" + age);
}
}
public class SuperDemo {
public static void main(String[] args) {
Child child = new Child("小明", 18);
child.show();
}
}
super()和this()调用构造方法总结:
- super():调用父类中的某一个构造方法(必须为构造方法中的第一条语句)。
- this():调用本类中某一个构造方法(必须为构造方法中的第一条语句)。
super()和this()都必须在第一条语句,就证明super()和this()不可以同时出现在同一个构造方法中,其原因是super()保证在子类访问父类之前完成对父类的初始化操作,而this()保证父类初始化的唯一性。
this和super 总结
this和super使用非常的相似,但是表现上却是大不相同,详情请看以下表格。
区别点 | this | super |
---|---|---|
定义 | this代表本类对象的引用 | super代表父类存储空间 |
使用 | this.属性 this.方法 this() | super.属性 super.方法() super() |
调用构造 | 调用本类构造,放在第一条语句 | 调用父类构造,放在第一条语句 |
查找范围 | 先从本类找,找不到查找父类 | 直接查找父类,不查找子类 |
7、final关键字
final 的意思为最终,不可变。final 是个修饰符,它可以用来修饰类、类中的属性和方法和局部变量,但是不能修饰构造方法。
final的特点
1、final 修饰类不可以被继承,但是可以继承其它类。
class AA {}
final class BB extends AA {} // final修饰类可以继承其他类
class CC extends BB {} // 编译错误,final修饰类不可以被继承
2、final 修饰的方法不可以被覆盖,但子类的方法可以用final修饰。
class AA {
final public void show1() {}
void public show2() {}
}
class BB extends AA {
final public void show1() {} // 编译错误,final修饰的方法不可以被覆盖
final public void show2() {} // 父类中没有被final修饰方法,子类覆盖后可以加final
}
3、final 修饰的变量称为常量,这些变量只能赋值一次。
final修饰成员变量和静态变量,必须显示的设置初始值,JVM不会设置默认值
常量命名规则:多个有意义的单词连接,所有字符大写,单词之间用下划线分割。
public class FinalDemo {
// 成员变量必须显示的设置初始值,JVM不会不会设置默认值。
final String USER_NAME = "xiaoming";
public static void main(String[] args) {
// 多个有意义的单词连接,所有字符大写,单词之间用下划线分割.
final int MAX_VALUE = Integer.MAX_VALUE;
// 编译错误,final 修饰的变量称为常量,这些变量只能赋值一次。
MAX_VALUE = 120;
}
}
4、final修饰的引用数据类型变量,变量中的地址不能改变,但是指向堆中对象的属性可以修改。
class Person {
String name = "小明";
}
public class FinalDemo {
public static void main(String[] args) {
final Person p = new Person();
p = new FinalDemo(); // 编译错误,地址值不能更改
p.name = "小花"; // 地址内的对象属性值可以修改
}
}