面向对象进阶部分学习方法:
特点:
逻辑性没有那么强,但是概念会比较多。
记忆部分重要的概念,理解课堂上讲解的需要大家掌握的概念,多多练习代码。
今日内容
- 复习回顾
- static关键字
- 继承
教学目标
第一章 复习回顾
1.1 如何定义类
类的定义格式如下:
修饰符 class 类名 {
// 1.成员变量(属性)
// 2.成员方法 (行为)
// 3.构造方法 (初始化类的对象数据的)
}
例如:
public class Student {
// 1.成员变量
public String name ;
public char sex ; // '男' '女'
public int age;
}
1.2 如何通过类创建对象
类名 对象名称 = new 类名();
例如:
Student stu = new Student();
1.3 封装
1.3.1 封装的步骤
1.使用 private
关键字来修饰成员变量。
2.使用public
修饰getter和setter方法。
1.3.2 封装的步骤实现
- 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;
}
}
1.4 构造方法
1.4.1 构造方法的作用
在创建对象的时候,可以给成员变量进行初始化。
初始化即赋值的意思。
1.4.2 构造方法的格式
修饰符 类名(形参列表) {
// 构造体代码,执行代码
}
1.4.3 构造方法的应用
首先定义一个学生类,代码如下:
public class Student {
// 1.成员变量
public String name;
public int age;
// 2.构造方法
public Student() {
System.out.println("无参数构造方法被调用");
}
}
接下来通过调用构造方法得到两个学生对象。
public class CreateStu02 {
public static void main(String[] args) {
// 创建一个学生对象
// 类名 变量名称 = new 类名();
Student s1 = new Student();
// 使用对象访问成员变量,赋值
s1.name = "张三";
s1.age = 20 ;
// 使用对象访问成员变量 输出值
System.out.println(s1.name);
System.out.println(s1.age);
Student s2 = new Student();
// 使用对象访问成员变量 赋值
s2.name = "李四";
s2.age = 18 ;
System.out.println(s2.name);
System.out.println(s2.age);
}
}
1.5 this关键字的作用
1.5.1 this关键字的作用
this代表所在类的当前对象的引用(地址值),即代表当前对象。
1.5.2 this关键字的应用
1.5.2.1 用于普通的gettter与setter方法
this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)。
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
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;
}
}
1.5.2.2 用于构造方法中
this出现在构造方法中,代表构造方法正在初始化的那个对象。
public class Student {
private String name;
private int age;
// 无参数构造方法
public Student() {}
// 有参数构造方法
public Student(String name,int age) {
this.name = name;
this.age = age;
}
}
面向对象—构建一个JavaBean对象
特征 1: 成员变量私有.2:提供get set。3:一般会有 空参 满参。
第二章 static关键字
2.1 概述
以前我们定义过如下类:
public class Student {
// 成员变量
public String name;
public char sex; // '男' '女'
public int age;
// 无参数构造方法
public Student() {
}
// 有参数构造方法
public Student(String a) {
}
}
我们已经知道面向对象中,存在类和对象的概念,我们在类中定义了一些成员变量,例如name,age,sex ,结果发现这些成员变量,每个对象都存在(因为每个对象都可以访问)。
而像name ,age , sex确实是每个学生对象都应该有的属性,应该属于每个对象。
所以Java中成员(变量和方法)等是存在所属性的,Java是通过static关键字来区分的。static关键字在Java开发非常的重要,对于理解面向对象非常关键。
关于 static
关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。
2.2 定义格式和使用
static是静态的意思。 static可以修饰成员变量或者修饰方法。
2.2.1 静态变量的引入
有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
如何使用呢
例如现在我们需要定义传智全部的学生类,那么这些学生类的对象的老师属性应该都是“石破天”,这个时候我们可以把这个属性定义成static修饰的静态成员变量。
定义格式
修饰符 static 数据类型 变量名 = 初始值;
举例
public class Student {
String name;//成员变量--实例变量
int age ;//成员变量--实例变量
static String teacherName;//静态变量 (共享变量)
public void study(){
System.out.println(name+"正在"+teacherName+
"指导下学习...");
}
}
静态成员变量的访问:
格式:类名.静态变量
/*
如果想定义 被多个对象 共享的成员 可以在 这个成员的前面加上
一个关键字 static
对于 静态成员访问 建议直接类名访问
因为 加了静态之后属于类 不属于某个对象
优先于对象出现的。
*/
public class Test {
public static void main(String[] args) {
Student.teacherName = "石破天老师";//访问静态变量
//创建学生对象
Student s1 = new Student();
s1.name="努力的迪迪";//不加static的变量属于对象
s1.age = 23;
s1.study();
Student s2 = new Student();
s2.study();
}
}
2.2.2 静态使用语法
总结
static关键字是什么?
static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量。
什么是静态变量,静态方法?
被static修饰的成员变量,叫静态变量。
被static修饰的成员方法,叫静态方法。
有什么特点?
静态成员不属于对象,但是可以被所有对象共享。
静态成员属于类,也叫类成员,随着类的加载而加载,优先于对象存在。
一般通过类名调用,也可以通过对象名调用,但是不推荐。
2.2.3 静态变量的应用案例
有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
学习完static修饰成员变量的基本使用之后,接下来我们学习一下static修饰成员变量在实际工作中的应用。
在实际开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来记住。
我们看一个案例
需求:系统启动后,要求用于类可以记住自己创建了多少个用户对象。**
- 第一步:先定义一个
User
类,在用户类中定义一个static修饰的变量,用来表示在线人数;
public class User{
public static int number;
//每次创建对象时,number自增一下
public User(){
User.number++;
}
}
- 第二步:再写一个测试类,再测试类中创建4个User对象,再打印number的值,观察number的值是否再自增。
public class Test{
public static void main(String[] args){
//创建4个对象
new User();
new User();
new User();
new User();
//查看系统创建了多少个User对象
System.out.println("系统创建的User对象个数:"+User.number);
}
}
运行上面的代码,查看执行结果是:系统创建的User对象个数:4
2.2.4 静态方法应用案例
学习完static修饰方法之后,我们讲一个有关类方法的应用知识,叫做工具类。
如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一下,所以把这样的类就叫做工具类。
package com.itheima.static03;
/*
ArrayUtil 数组工具类
里面提供了 操作数组的方法
为什么叫工具类呢?
区别于javabean类的,不表示任何事物。
里面只是定义 操作数组的方法!!
因为主要是来调用方法,所以对象不重要,所以这里方法都是静态的,构造私有的。
工具类特点
1: 构造私有。
2: 方法静态。
3: 见名知意。
*/
public class ArrayUtil {
// 构造私有的目的 不叫别人创建对象
private ArrayUtil(){}
/*
设计方法
提供一个 打印方法 printArr 用于返回整数数组的内容。
一个模板 两个明确
明确参数列表 :
整数数组 int[]
明确返回值类型
String
实现方法:
int[] arr = {1,2,3,4};
通过方法
产出一个字符串 [1,2,3,4]
拼接字符串
*/
public static String printArr(int[] arr){
// 定义一个 初始字符串
String str = "[";
// 循环遍历 进行字符串拼接
for (int i = 0; i < arr.length; i++) {
// arr[i]每个元素
if(i== arr.length-1){//是最后一个元素
str += arr[i];//不要逗号
}else {//不是最后一个元素
str += arr[i]+",";//要逗号
}
}
// 拼接末尾
str += "]";
// 拼接完成再返回
return str;
}
/*
提供一个getAverage 用于返回平均分
求 浮点型数组 的 平均分
明确 返回值 平均分 double
明确 参数 浮点型数组 double[]
*/
public static double getAverage(double[] arr){
// 求和 套路
// 定义求和变量
double sum = 0;
// 遍历 进行累加
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
// 处理 求和变量
double avg = sum/arr.length;
return avg;
}
}
package com.itheima.static03;
import java.util.Arrays;
public class TestDemo {
public static void main(String[] args) {
//测试 printArr方法 将一个整数数组 转换成 字符串
// 定义 整数数组
int[] arr = {9,5,2,7};
//调用 的 工具方法 完成转换
String s = ArrayUtil.printArr(arr);
System.out.println(s);
//测试getAverage 获取平均分方法
double[] arr2 = {98.5,97.5,99.5,0};
double average = ArrayUtil.getAverage(arr2);
System.out.println(average);
//刚刚自己写的工具类---玩一次就行
String s1 = Arrays.toString(arr);
System.out.println(s1);
}
}
总结
类方法有啥应用场景?
可以用来设计工具类。
工具类是什么,有什么好处?
工具类中的方法都是类方法,每个类方法都是用来完成一个功能的。
提高了代码的复用性;调用方便,提高了开发效率。
为什么工具类要用类方法,而不是用实例(普通成员)方法?
实例方法需要创建对象来调用,会浪费内存。
工具类定义时有什么要求?
工具类不需要创建对象, 建议将工具类的构造器私有化
2.2.4 类成员 实例成员辨析
Java中的成员按照有无static修饰分成两种:类成员,实例成员。
有static修饰的,被称为类成员,属于类:比如
- 类变量:也叫静态变量,有static修饰,属于类,计算机里只有一份,会被类的全部对象共享。
- 类方法:也叫静态方法,有static修饰的方法,属于类,可以通过类名直接调用。
无static修饰的,被称为实例成员,属于对象:比如
- 实例变量:属于对象,每个对象中都有一份,只能用对象访问。
- 实例方法:无static修饰的成员方法,属于对象,只能对象访问。
public class People {
// 静态变量
static String name;
// 实例变量
int age ;
public static void show1(){
System.out.println("这是一个静态方法");
}
public void show2(){
System.out.println("这是一个实例方法");
}
}
访问:
package com.itheima.static02;
/*
加入了静态之后
方法和变量访问 可以直接通过类
不用创建对象情况下 完成方法和变量调用。
*/
public class Test {
public static void main(String[] args) {
//静态变量 直接类名调用
People.name = "jackMa";
//静态方法 类名直接调用
People.show1();
// 静态内容 被对象 共享
// People.show2(); 类无法调用实例内容
}
}
2.2.5 static使用注意事项
- 静态方法只能访问静态变量和静态方法
- 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
- 静态方法中是没有this关键字
演示–了解目的
package com.itheima.static04notice;
public class People {
static String name;// 静态变量--类变量
int age;//成员变量--实例变量
//静态方法--类方法
public static void show1(){
System.out.println("这是一个静态方法");
System.out.println(name);//静态方法中 访问静态变量
method1();//静态方法中 可以调用静态方法
// System.out.println(age);无法访问非静态
// show2();无法访问非静态
}
//非静态--普通方法--实例方法
public void show2(){
System.out.println("这是一个实例方法");
System.out.println(name);//可以访问静态
method1();
}
public static void method1(){}
}
2.2.6 单例模式
各位同学,关于static的应用我们再补充一个使用的技巧,叫做单例设计模式。
所谓设计模式指的是,一类问题可能会有多种解决方案,而设计模式是在编程实践中,多种方案中的一种最优方案。
示例:
package com.itheima.static05;
/*
保证 程序中 有且只有 一个app对象
合理隐藏
1:私有构造(不让外界new)
合理暴露
2:new 出一个对象 在当前类中
定义为私有的静态变量 因为类加载一次 静态变量初始化一次。
3:如何暴露出来
提供get方法---也得是静态
饿汉式
立即加载,当类产生的时候,对象就产生了,不管你用不用对象。
大学食堂
(用于--访问量大的对象。)
懒汉式 --多线程补充
延迟加载,当类产生的时候,不创建对象。需要再创建。
小饭馆
(用于--个性的 访问量小的)
*/
public class App {
private static App app = new App();//实例变量? 静态变量?
// 保证new的事情 做一次 静态变量 随着类的加载而加载 一次
private App(){}
public static App getApp(){
return app;
}
}
package com.itheima.static05;
public class Phone {
public static void main(String[] args) {
//需求 打开 app的时候 只允许在内存中运行一个对象
App a1 = App.getApp();
App a2 = App.getApp();
App a3 = App.getApp();
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
}
}
2.3 小结
1.当 static
修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
第三章 继承
3.1 概述
3.1.1 引入
假如我们要定义如下类:
学生类,老师类和工人类,分析如下。
- 学生类
属性:姓名,年龄
行为:吃饭,睡觉 - 老师类
属性:姓名,年龄,薪水
行为:吃饭,睡觉,教书 - 班主任
属性:姓名,年龄,薪水
行为:吃饭,睡觉,管理
如果我们定义了这三个类去开发一个系统,那么这三个类中就存在大量重复的信息(属性:姓名,年龄。行为:吃饭,睡觉)。这样就导致了相同代码大量重复,代码显得很臃肿和冗余,那么如何解决呢?
假如多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。如图所示:
其中,多个类可以称为子类,单独被继承的那一个类称为父类、超类(superclass)或者基类。
3.1.2 继承的含义
继承描述的是事物之间的所属关系,这种关系是:is-a
的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
继承:就是子类继承父类的属性和行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
3.1.3 继承的好处
- 提高代码的复用性(减少代码冗余,相同代码重复利用)。
- 使类与类之间产生了关系。
3.1.4 继承的格式
通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {
...
}
class 子类 extends 父类 {
...
}
需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。
3.2 继承案例
各位同学,学习完继承的快速入门之后,接下来我们学习一下继承的好处,以及它的应用场景。
我们通过一个案例来学习
观察代码发现,我们会发现Teacher类中和Consultant类中有相同的代码;其实像这种两个类中有相同代码时,没必要重复写。
我们可以把重复的代码提取出来,作为父类,然后让其他类继承父类就可以了,这样可以提高代码的复用性。改造后的代码如下:
接下来使用继承来完成上面的案例,这里只演示People类和Teacher类,然后你尝试自己完成Consultant类。
- 先写一个父类 People,用来设计Teacher和Consultant公有的成员。
public class People{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
}
- 再写两个子类Teacher继承People类,同时在子类中加上自己特有的成员。
public class Teacher extends People{
private String skill; //技能
public String getSkill(){
return skill;
}
public void setSkill(String skill){
this.skill=skill;
}
public void printInfo(){
System.out.println(getName()+"具备的技能:"+skill);
}
}
- 最后再写一个测试类,再测试类中创建Teacher、Consultant对象,并调用方法。
public class Test {
public static void main(String[] args) {
// 目标:搞清楚继承的好处。
Teacher t = new Teacher();
t.setName("播仔");
t.setSkill("Java、Spring");
System.out.println(t.getName());
System.out.println(t.getSkill());
t.printInfo();
}
}
执行代码,打印结果如下:
关于继承的好处我们只需要记住:继承可以提高代码的复用性
3.3 权限修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和默认修饰符的作用。
- public:公共的,所有地方都可以访问。
- protected:本类 ,本包,其他包中的子类都可以访问。
- 默认(没有修饰符):本类 ,本包可以访问。
注意:默认是空着不写,不是default - private:私有的,当前类可以访问。
public > protected > 默认 > private
public | protected | 默认 | private | |
同一类中 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用
private
,隐藏细节。 - 构造方法使用
public
,方便创建对象。 - 成员方法使用
public
,方便调用方法。
小贴士:不加权限修饰符,就是默认权限
3.4 单继承 Object
刚才我们写的代码中,都是一个子类继承一个父类,那么有同学问到,一个子类可以继承多个父类吗?
Java语言只支持单继承,不支持多继承,但是可以多层继承。就像家族里儿子、爸爸和爷爷的关系一样:一个儿子只能有一个爸爸,不能有多个爸爸,但是爸爸也是有爸爸的。
public class Test {
public static void main(String[] args) {
// 目标:掌握继承的两个注意事项事项。
// 1、Java是单继承的:一个类只能继承一个直接父类;
// 2、Object类是Java中所有类的祖宗。
A a = new A();
B b = new B();
ArrayList list = new ArrayList();
list.add("java");
System.out.println(list.toString());
}
}
class A {} //extends Object{}
class B extends A{}
// class C extends B , A{} // 报错
class D extends B{}
3.5 方法重写
各位同学,学习完继承之后,在继承的基础之上还有一个很重要的现象需要给大家说一下。
叫做方法重写。为了让大家能够掌握方法重写,我们先认识什么是方法重写,再说一下方法的应用场景。
什么是方法重写
当子类觉得父类方法不好用,或者无法满足父类需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
注意:重写后,方法的访问遵循就近原则。下面我们看一个代码演示
例如:我们定义了一个动物类代码如下:
public class Animal {
public void run(){
System.out.println("动物跑的很快!");
}
public void cry(){
System.out.println("动物都可以叫~~~");
}
}
然后定义一个猫类,猫可能认为父类cry()方法不能满足自己的需求
代码如下:
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();
}
}
执行代码,我们发现真正执行的是Cat类中的cry方法
知道什么是方法重写之后,还有一些注意事项,需要和大家分享一下。
- 1.重写的方法上面,可以加一个注解@Override,用于标注这个方法是复写的父类方法
- 2.子类复写父类方法时,访问权限必须大于或者等于父类方法的权限
public > protected > 缺省
- 3. 重写的方法返回值类型,必须与被重写的方法返回值类型一样,或者范围更小
- 4. 私有方法、静态方法不能被重写,如果重写会报错。
关于这些注意事项,同学们其实只需要了解一下就可以了。实际上我们实际写代码时,只要和父类写的一样就可以( 总结起来就8个字:声明不变,重新实现)
方法重写的应用场景
学习完方法重写之后,接下来,我们还需要大家掌握方法重写,在实际中的应用场景。方法重写的应用场景之一就是:子类重写Object的toString()方法,以便返回对象的内容。
比如:有一个Student类,这个类会默认继承Object类。
public class Student extends Object{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
}
其实Object类中有一个toString()方法,直接通过Student对象调用Object的toString()方法,会得到对象的地址值。
public class Test {
public static void main(String[] args) {
Student s = new Student("播妞", 19);
// System.out.println(s.toString());
System.out.println(s);
}
}
但是,此时不想调用父类Object的toString()方法,那就可以在Student类中重新写一个toSting()方法,用于返回对象的属性值。
package com.itheima.extends05;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 快捷键 toS 回车
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
重新运行测试类,结果如下
好了,到这里方法什么是方法重写,以及方法重写的应用场景我们就学习完了。
package com.itheima.extends06;
public class Dog {
public void eat(){
System.out.println("吃狗粮");
}
public void drink(){
System.out.println("狗舔水..");
}
public void lookDoor(){
System.out.println("狗子看家");
}
}
package com.itheima.extends06;
public class EnZuo extends Dog{
//不重写
//特有功能
public void chaiJia(){
System.out.println("二哈拆家");
}
}
package com.itheima.extends06;
public class PiPi extends Dog{
@Override
public void eat() {
super.eat();//调用父类的 eat方法
System.out.println("吃骨头");
}
}
package com.itheima.extends06;
public class BaJie extends Dog{
@Override
public void eat() {
System.out.println("吃剩饭..");
}
}
package com.itheima.extends06;
public class Demo {
public static void main(String[] args) {
PiPi pp = new PiPi();
pp.eat();
pp.drink();
pp.lookDoor();
}
}
3.6 继承后的特点—子类中访问成员的特点
各位同学,刚才我们已经学习了继承,我们发现继承至少涉及到两个类,而每一个类中都可能有各自的成员(成员变量、成员方法),就有可能出现子类和父类有相同成员的情况,那么在子类中访问其他成员有什么特点呢?
- 原则:在子类中访问其他成员(成员变量、成员方法),是依据就近原则的
定义一个父类,代码如下
package com.itheima.extends07;
public class Fu {
int num = 100 ;
public void method(){
System.out.println("父method");
}
}
再定义一个子类,代码如下。有一个同名的num成员变量,有一个同名的method成员方法;
package com.itheima.extends07;
public class Zi extends Fu{
int num = 200;
public void show(){
int num = 300;
System.out.println(num);//300
System.out.println(this.num);//200
System.out.println(super.num);//100
//访问method
this.method();
//父类的method
super.method();
}
@Override
public void method() {
System.out.println("子method");
}
}
接下来写一个测试类,观察运行结果,我们发现都是调用的子类变量、子类方法。
package com.itheima.extends07;
/*
理解
1:子类方法中访问成员的时候,遵循就近原则。
变量--局部位置找--成员位置--父类成员位置
方法--当前类中找---父类中找
2:
this表示当前对象,
通过它可以访问成员变量 成员方法
super 父类的标识
通过它可以访问 父类的成员变量 成员方法
*/
public class Demo {
public static void main(String[] args) {
//创建子类对象
Zi z = new Zi();
z.show();
}
}
- 如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加this或者super进行区分。
3.7 继承后的特点—构造方法
3.7.1 引入
当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先我们要回忆两个事情,构造方法的定义格式和作用。
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个
super()
,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子)
继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法
3.7.2 案例演示
代码如下:
package com.itheima.extends08;
public class Fu {
public Fu(){
System.out.println("fu的无参构造~");
}
public Fu(String name){
System.out.println("父的有参构造");
}
}
package com.itheima.extends08;
public class Zi extends Fu{
public Zi(){
// 先执行了父类的无参构造...
// super();
// 调用父类的带参构造
super("haha");
System.out.println("子的无参构造");
}
public Zi(String name){
// 先执行了父类的无参构造
System.out.println("子的带参构造");
}
}
package com.itheima.extends08;
public class Demo {
/*
理解
1:子类构造执行时,默认会先执行父类的无参构造。
子类构造的第一行 默认是 super()
访问父类的构造
为什么有这个情况,因为我们想在子类对象构建的时候
先初始化父类成员。
所以在执行子类构造的时候,
调用了父类的无参构造。
2: 怎么调用父类的有参构造呢?
super() 无参构造
super(...)
*/
public static void main(String[] args) {
Zi zi1 = new Zi();
Zi zi2 = new Zi("jack");
}
}
3.7.3 小结
- 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
- 子类构造方法的第一行都隐含了一个**super()**去调用父类无参数构造方法,**super()**可以省略不写。
3.8 super(…)和this(…)
3.8.1 引入
请看上节中的如下案例:
package com.itheima.thisuper;
public class Fu {
private String name;
public Fu(String name){
this.name = name;
}
}
package com.itheima.thisuper;
public class Zi extends Fu{
public Zi(){
//super() 访问父类有参
// super("哈哈");
this("哈哈");
}
public Zi(String name){
super(name);
}
}
我们发现,通过super(…)可以访问父类带参构造,this(…)可以访问本类的构造.
3.8.2 super和this的用法格式
super和this完整的用法如下,其中this,super访问成员我们已经接触过了。
this.成员变量 -- 本类的
super.成员变量 -- 父类的
this.成员方法名() -- 本类的
super.成员方法名() -- 父类的
接下来我们使用调用构造方法格式:
super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认
3.8.3 super(…)案例图解
父类空间优先于子类对象产生
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造七调用时,一定先调用父类的构造方法。理解图解如下:
3.8.4 小结
- 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
- super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
- super(…)和this(…)是根据参数去确定调用父类哪个构造方法的。
- super(…)可以调用父类构造方法初始化继承自父类的成员变量的数据。
- this(…)可以调用本类中的其他构造方法。
4. 关于今天知识的小结:
会写一个继承结构下的标准Javabean即可
- 经理
成员变量:工号,姓名,工资,管理奖金
成员方法:工作(管理其他人),吃饭(吃米饭) - 厨师
成员变量:工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
分享书写技巧:
1.在大脑中要区分谁是父,谁是子
2.把共性写到父类中,独有的东西写在子类中
3.开始编写标准Javabean(从上往下写)
4.在测试类中,创建对象并赋值调用
代码示例:
员工类
package com.itheima.test;
/*
员工类 作为父类出现
*/
public class Employee {
private String id;//工号
private String name;//姓名
private double salary;//工资
//生成满参空参构造
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
//生成set get方法
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//工作方法 吃饭方法
public void work(){
System.out.println("员工都在干活....");
}
public void eat(){
System.out.println("员工都在吃米饭!!!");
}
}
管理类
package com.itheima.test;
/*
经理类 继承 员工类
*/
public class Manager extends Employee {
//特有成员变量
private double bonus;
//写构造
// 子父类--产生了基础 父类还有成员变量
// 构造 子类无参调用父类无参 子类带参调用父类带参
// 只有这样才能实现数据的初始化
//alt+insert 找到构造 选择 空参的 select none
public Manager() {
}
//alt+insert 找到构造 选择带参 后面 ok
public Manager(String id, String name, double salary, double bonus) {
super(id, name, salary);//三个给父类
this.bonus = bonus;//一个给自己
}
//set get
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
//重写 work
@Override
public void work() {
System.out.println(getName()+"正在管理其他人..."+"管理奖金:"+getBonus());
}
}
厨师类
package com.itheima.test;
/*
作为子类 继承自 父类员工
*/
public class Cooker extends Employee{
//无参和带参
public Cooker() {
}
public Cooker(String id, String name, double salary) {
super(id, name, salary);//调用父类带参构造
}
@Override
public void work() {
System.out.println("工号为"+getId()+"厨师:"+getName()+"正在炒菜...."+"工资是:"+getSalary());
}
}
测试类
package com.itheima.test;
public class Test {
public static void main(String[] args) {
Cooker cooker = new Cooker("9527","华安",1000);
cooker.work();
//测试 经理
Manager m = new Manager("001", "吴宇航", 100000, 1000000);
m.work();
}
}