目录
首先可以复习一下我们上一篇有关于Java对于类的介绍以及定义
接下来我们要介绍以下关于Java里的一个比较重要的性能,也就是所谓继承。但在这之前需要对于上一节课的类以及封装进行一个深度了解。
封装的步骤
1.使用 `private` 关键字来修饰成员变量。
2.使用`public`修饰getter和setter方法。
private修饰成员变量
public class Student {
private String name;
private int age;
}
public修饰getter和setter方法
public class Student {
private String name;
private int age;
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int a) {
if (a > 0 && a <200) {
age = a;
} else {
System.out.println("年龄非法!");
}
}
public int getAge() {
return age;
}
}
构造方法
public class Student {
//成员变量 私有化成员变量
public String name;
public int age;
//构造方法 记得要进行公开
public Student() {
System.out.println("无参数构造方法被调用");
}
this关键字
this代表所在类的当前对象的引用(地址值),即代表当前对象。
我们将this换一句话也就是this指向的变量是我们的成员变量
this关键字的应用
用于普通的gettter与setter方法
this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)。
我们在实例方法中,注意是在一个方法内部我们是进行对于this进行调用,谁调用这个对象,我们的这个this就会代表哪个对象
对于代码的解释
public class Student {
private String name;//成员变量
private int age;//成员变量
//set方法
public void setName(String name) {
this.name = name;
}
//get方法
public String getName() {
return name;
}
public void setAge(int age) {
if (age > 0 && age < 200) {
this.age = age;
} else {
System.out.println("年龄非法!");
}
}
public int getAge() {
return age;
}
}
我们可以发现,在我们的set方法里面,我们都会有一个this方法,而这个方法的意思就是直接指向我们的成员变量,换一句话说,我们的等式左侧的意思是直接指向我们所有的成员变量,而右边的数值的意思是指向我们要将成员变量需要修改的数值,换一句话说,也就是我们要将成员变量直接变成我们在方法内部输入的数值
static关键字
public class Student {
// 成员变量
public String name;
public char sex; // '男' '女'
public int age;
// 无参数构造方法
public Student() {
}
// 有参数构造方法
public Student(String a) {
}
}
在这里我们需要对内存有一个理解,再Java中变量和方法是存在所属性的,而Java是通用static关键字来区分的。
关于 `static` 关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。
我们需要补充的是static会将我们所修饰的成员或者是方法单独开出来一个静态区用来存放我们被修饰的变量以及方法
static定义
static是静态的意思。 static可以修饰成员变量或者修饰方法。
如何访问static
有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为**类变量**或者**静态成员变量**。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
从内存的角度理解,我们是将方法或者是变量存入一个单独的区域,在内存中有一块是专门给静态变量存储的,并且在这个区域我们可以进行调用这个变量或者方法,相当于是为了所有对象提供了这个变量,并且直接用类名调用即可
定义格式
修饰符 static 数据类型 变量名 = 初始值;
public class Animal {
public static String AnimalName = "Nick"; // 属于类,只有一份。
// .....
对于Animal的调用
public static void main(String[] args){
System.out.println(Animal.AnimalName);
Student.AnimalName = "Nick";
System.out.println(Animal.AnimalName);
}
实例变量及其访问
无static修饰的成员变量属于每个对象的, 这个成员变量叫**实例变量**,之前我们写成员变量就是实例成员变量。
public class Student {
// 实例变量
private String name ;
// 2.方法:行为
// 无 static修饰,实例方法。属于每个对象,必须创建对象调用
public void run(){
System.out.println("学生可以跑步");
}
// 无 static修饰,实例方法
public void sleep(){
System.out.println("学生睡觉");
}
public static void study(){
}
}
public static void main(String[] args){
// 创建对象
Student stu = new Student();
stu.name = "陈11";
// Student.sleep();// 报错,必须用对象访问。
stu.sleep();
stu.run();
}
在这里我们就可以发现一件事情
1. 静态方法不能调用非静态的方法和变量。
2.不能使用this和super关键字。
3.static所修饰的方法或者是变量都是属于类的级别
4.在这里我们还需要通过上面的例子发现一点,对于想要调用没有static修饰的成员方法,我们必须需要先创建一个对象,然后通过对象去调用该方法,但是在static中进行修饰的成员变量,我们可以直接通过类进行调用,因为我们认为static是在类的级别上
继承
为什么需要继承的存在
1. 学生类
属性:姓名,年龄
行为:吃饭,睡觉
2. 老师类
属性:姓名,年龄,薪水
行为:吃饭,睡觉,教书
3. 班主任
属性:姓名,年龄,薪水
行为:吃饭,睡觉,管理
我们会发现,重复了太多的冗余,比如老师类学生类以及班主任,都会有一个共同的特性也就是,年龄,姓名,吃饭,睡觉,所以在调用的时候,如果在每个类里都要调用就会很麻烦,所以我们需要进行将这些进行合并,而这也就引了继承的概念
继承的定义
继承描述的是事物之间的所属关系,这种关系是:`is-a` 的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
继承:就是子类继承父类的**属性**和**行为**,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的**非私有**的属性和行为。
继承的格式
class 父类 {
...
}
class 子类 extends 父类 {
...
}
**需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。**
定义一个Human类是他们共有的父亲
public class Human {
// 合理隐藏
private String name ;
private int age ;
// 合理暴露
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;
}
}
Teacher子类
public class Teacher extends Human {
// 工资
private double salary ;
// 特有方法
public void teach(){
System.out.println("老师在认真教技术!");
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
Student子类
public class Student extends Human{
}
测试
public class Test {
public static void main(String[] args) {
Teacher dlei = new Teacher();
dlei.setName("陈11");
dlei.setAge("19");
dlei.setSalary(9999.99);
System.out.println(dlei.getName());
System.out.println(dlei.getAge());
System.out.println(dlei.getSalary());
dlei.teach();
BanZhuRen linTao = new BanZhuRen();
linTao.setName("陈22");
linTao.setAge("20");
linTao.setSalary(8888.88);
System.out.println(linTao.getName());
System.out.println(linTao.getAge());
System.out.println(linTao.getSalary());
linTao.admin();
Student xugan = new Student();
xugan.setName("陈33");
xugan.setAge("21");
//xugan.setSalary(8888.88); // xugan没有薪水属性,报错!
System.out.println(xugan.getName());
System.out.println(xugan.getAge());
}
}
通过上述我们可以发现,子类可以继承父类的公开的方法,换一句话说,我们子类可以继承父类里的公开的方法,无法继承父类里面的私密的方法,而在子类里,只需要补充子类自己独有的方法即可,换句话说,子类继承父类,子类自己补充子类。
子类无法引入的内容
子类不能继承父类的构造方法。
值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
public class Demo03 {
public static void main(String[] args) {
Zi z = new Zi();
System.out.println(z.num1);
// System.out.println(z.num2); // 私有的子类无法使用
// 通过getter/setter方法访问父类的private成员变量
System.out.println(z.getNum2());
z.show1();
// z.show2(); // 私有的子类无法使用
}
}
class Fu {
public int num1 = 10;
private int num2 = 20;
public void show1() {
System.out.println("show1");
}
private void show2() {
System.out.println("show2");
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
}
class Zi extends Fu {
}
关于子父不重名
class Fu {
// Fu中的成员变量
public int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num2 = 6;
// Zi中的成员方法
public void show() {
// 访问父类中的num
System.out.println("Fu num="+num); // 继承而来,所以直接访问。
// 访问子类中的num2
System.out.println("Zi num2="+num2);
}
}
class Demo04 {
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show方法
z.show();
}
}
Fu num = 5
Zi num2 = 6
这个是我们的打印结果,说明子类直接进行继承父亲的名称
子父同名
class Fu1 {
// Fu中的成员变量。
int num = 5;
}
class Zi1 extends Fu1 {
// Zi中的成员变量
int num = 6;
public void show() {
// 访问父类中的num
System.out.println("Fu num=" + num);
// 访问子类中的num
System.out.println("Zi num=" + num);
}
}
class Demo04 {
public static void main(String[] args) {
// 创建子类对象
Zi1 z = new Zi1();
// 调用子类中的show方法
z1.show();
}
}
Fu num = 6
Zi num = 6
子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。如果此时想访问父类成员变量如何解决呢?我们可以使用super关键字。
super关键字
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用`super` 关键字,修饰父类成员变量,类似于之前学过的 `this` 。
class Fu {
// Fu中的成员变量。
int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
int num = 1;
// 访问方法中的num
System.out.println("method num=" + num);
// 访问子类中的num
System.out.println("Zi num=" + this.num);
// 访问父类中的num
System.out.println("Fu num=" + super.num);
}
}
class Demo04 {
public static void main(String[] args) {
// 创建子类对象
Zi1 z = new Zi1();
// 调用子类中的show方法
z1.show();
}
}
method num=1
Zi num=6
Fu num=5
> 小贴士:Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
方法重写
**方法重写** :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。**声明不变,重新实现**。
在哪里使用重写比较合适呢
发生在子父类之间的关系。
子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方 法。
public class Animal {
public void run(){
System.out.println("动物跑的很快!");
}
public void cry(){
System.out.println("动物都可以叫~~~");
}
}
public class Cat extends Animal {
public void cry(){
System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
}
}
public class Test {
public static void main(String[] args) {
// 创建子类对象
Cat ddm = new Cat();
// 调用父类继承而来的方法
ddm.run();
// 调用子类重写的方法
ddm.cry();
}
}
@Override重写注解
@Override:注解,重写注解校验!
* 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
* 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
对上述代码进行修改
public class Cat extends Animal {
// 声明不变,重新实现
// 方法名称与父类全部一样,只是方法体中的功能重写写了!
@Override
public void cry(){
System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
}
}
1. 方法重写是发生在子父类之间的关系。
2. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
3. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
关于权限的补充(public>protect>normal>private
构造方法
1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
2. 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个`super()` ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(**先有爸爸,才能有儿子**)1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
关于super和this的用法
this.成员变量 -- 本类的
super.成员变量 -- 父类的
this.成员方法名() -- 本类的
super.成员方法名() -- 父类的
super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认
* **子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。**
* **super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。**
* **super(..)和this(...)是根据参数去确定调用父类哪个构造方法的。**
* super(..)可以调用父类构造方法初始化继承自父类的成员变量的数据。
* this(..)可以调用本类中的其他构造方法。
对于继承的补充
* **子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。**
* **super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。**
* **super(..)和this(...)是根据参数去确定调用父类哪个构造方法的。**
* super(..)可以调用父类构造方法初始化继承自父类的成员变量的数据。
* this(..)可以调用本类中的其他构造方法。