JAVASE基础-特性


JAVASE基础-特性

面向对象

对于C语言来说,是完全面向过程的。

对于Java语言来说,是完全面向对象的。

采用面向对象的方式进行开发:耦合度低,扩展力强。

三个术语OOA/OOD/OOP:

① OOA:面向对象分析(Object-Oriented Analysis)

② OOD:面向对象设计(Object-Oriented Design)

③ OOP:面向对象编程(Object-Oriented Programming)
整个软件开发的过程,都是采用OO进行贯穿的。

三大特征

封装,继承,多态。

类和对象

解释:

将现实世界中某些事物具有的共同特征提取出来形成的概念就是一个“类”,实际存在的个体是对象。

定义类的语法格式

    [修饰符列表] class 类名 {
        //类体 = 属性 + 方法
        // 属性在代码上以“变量”的形式存在(描述状态)
        // 方法描述动作/行为
    }

对象的创建和使用

类是模板,通过一个类,可以创建N多个对象。new是一个运算符,专门负责对象的创建。java中所有的“类”都属于引用数据类型。(类近似C中结构体)

创建对象:

1
new 类名();

使用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
文件XueSheng.java:
public class XueSheng{ // 定义XueSheng类,编写成员变量作为属性。这个程序编译之后,会生成XueSheng.class字节码文件。
// 属性
// 成员变量中的实例变量
int xueHao;
String xingMing;
int nianLing;
boolean xingBie;
String zhuZhi;
}
文件XueShengTest.java
public class XueShengTest{
public static void main(String[] args){
XueSheng s1 = new XueSheng();
/*
s1是变量名(s1不能叫做对象。s1只是一个变量名字。)
XueSheng是变量s1的数据类型(引用数据类型)
new XueSheng() 这是一个对象。(学生类创建出来的学生对象。)
*/
}
}

应先编译XueSheng.java,再编译XueShengTest.java。但是对于编译器来说,编译XueShengTest.java文件的时候,会自动找XueSheng.class,如果没有,会自动编译XueSheng.java文件,生成XueSheng.class文件。

对象的创建和使用

对象的创建引用

类名 变量名 = new 类名();

但凡是变量,该变量中保存了指向堆内存当中的对象的内存地址。

参数传递

方法调用时参数的传递:复制值传递。(内存也是值)

基本数据类型:传递值

引用数据类型:传递内存地址(类似指针)

引用数据类型例,以下结果均为11:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test2{
public static void main(String[] args){
Person p = new Person();
p.age = 10;
add(p);
System.out.println("main--->" + p.age); //11
}
// 方法的参数可以是基本数据类型,也可以是引用数据类型,只要是合法的数据类型就行。
public static void add(Person p){ // p是add方法的局部变量。
p.age++;
System.out.println("add--->" + p.age); //11
}
}
class Person{
// 年龄属性,成员变量中的实例变量。
int age;
}

构造方法Constructor

构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建,以及实例变量的初始化。

当一个类中没有提供任何构造方法,系统默认提供一个无参数的构造方法。这个无参数的构造方法叫做缺省构造器。

构造方法是支持方法重载的。

实例变量在构造方法中没有手动赋值时,默认赋系统值。建议将无参数构造方法手动的写出来,这样一定不会出问题。

调用构造方法语法格式:

1
new 构造方法名(实际参数列表);

构造方法的语法结构:

1
2
3
4
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
//通常在构造方法体当中给属性赋值,完成属性的初始化。
}

注意:
第一:修饰符列表目前统一写:public。千万不要写public static。

​ 第二:构造方法名和类名必须一致。

​ 第三:构造方法不需要指定返回值类型,也不能写void,写上void表示普通方法,就不是构造方法了。

使用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Vip{
long no;
String name;
String birth;
boolean sex;
//无参数构造方法
public Vip(){
}
//有参数构造方法
public Vip(long huiYuanHao, String xingMing){
// 给实例变量赋值【初始化实例变量,初始化属性】
no = huiYuanHao;
name = xingMing;
// 实际上这里还有两行代码(没有手动赋值,系统都会默认赋值。)
//birth = null;
//sex = false;
}
//有参数构造方法
public Vip(long huiYuanHao,String xingMing,String shengRi,boolean xingBie){
no = huiYuanHao;
name = xingMing;
birth = shengRi;
sex = xingBie;
}
}

封装

一个类体当中的数据封装之后,对于代码的调用人员来说,不需要关心代码的复杂实现,只需要通过一个简单的入口就可以访问了。另外,将类体中安全级别较高的数据封装起来,外部人员不能随意访问,保证数据的安全性。

具体步骤:

第一步:属性私有化(使用private关键字进行修饰。)
第二步:对外提供简单的操作入口。

使用例:

定义Person类Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Person{
// private 表示私有的,被这个关键字修饰之后,该数据只能在本类中访问。
// 出了这个类,age属性就无法访问了。私有的。
private int age; // 每一个人年龄值不同,对象级别的属性。
// 对外提供简单的访问入口
// 外部程序只能通过调用以下的代码来完成访问
// 写一个方法专门来完成读。(get)
// 写一个方法专门来完成写。(set)
// get读年龄,set改年龄,这个读和改都是操作的一个对象的年龄。
// 封装的第二步:对外提供公开的set方法和get方法作为操作入口。并且都不带static。都是实例方法。
/*
[修饰符列表] 返回值类型 方法名(形式参数列表){
}
*/
// get方法
public int getAge(){
return age;
}
// set方法
public void setAge(int nianLing){
// 设置关卡
if(nianLing < 0 || nianLing > 150){
System.out.println("对不起,年龄值不合法,请重新赋值!");
return; //直接终止程序的执行。
}
//程序能够执行到这里,说明年龄一定是合法的。
age = nianLing;
}
}

注意:
java开发规范中有要求,set方法和get方法要满足以下格式。

1
2
3
4
5
6
7
8
get方法的要求:
public 返回值类型 get+属性名首字母大写(无参){
return xxx;
}
set方法的要求:
public void set+属性名首字母大写(有1个参数){
xxx = 参数;
}

this和static

static

​ 1、static翻译为“静态”
​ 2、所有static关键字修饰的都是类相关的,类级别的。
​ 3、所有static修饰的,都是采用“类名.”的方式访问。
​ 4、static修饰的变量:静态变量
​ 5、static修饰的方法:静态方法

变量的分类:

​ 变量根据声明的位置进行划分:
​ 在方法体中声明的变量叫做:局部变量。
​ 在方法体外声明的变量叫做:成员变量。

​ 成员变量又可以分为:
​ 实例变量
​ 静态变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class VarTest{
// 以下实例的,都是对象相关的,访问时采用“引用.”的方式访问。需要先new对象。
// 实例相关的,必须先有对象,才能访问,可能会出现空指针异常。
// 成员变量中的实例变量
int i;
// 实例方法
public void m2(){
// 局部变量
int x = 200;
}
// 以下静态的,都是类相关的,访问时采用“类名.”的方式访问。不需要new对象。
// 不需要对象的参与即可访问。没有空指针异常的发生。
// 成员变量中的静态变量
static int k;
// 静态方法
public static void m1(){
// 局部变量
int m = 100;
}
}

变量在内存中的储存位置:

局部变量:栈区

实例变量:堆区

静态变量:方法区

什么时候变量声明为实例的,什么时候声明为静态的?

​ 如果这个类型的所有对象的某个属性值都是一样的,不建议定义为实例变量,浪费(堆区)内存空间。建议定义为类级别特征,定义为静态变量在方法区中只保留一份,节省内存开销。

访问

实例相关的都需要new对象,使用”引用.”来访问。

静态方法不需要new对象,直接使用“类名.”来访问,但是也可以使用“引用.”来访问,不建议用,易造成维护迷惑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class StaticTest04{
public static void main(String[] args){
// 这是比较正规的方式,静态方法采用“类名.”
StaticTest04.doSome();
//对象
StaticTest04 st = new StaticTest04();
// 用“引用.”访问
st.doSome();
// 空引用
st = null;
// 不会出现空指针异常
st.doSome(); // 这个代码在最终执行的时候还是会转变为:StaticTest04.doSome();
StaticTest04 st2 = new StaticTest04();
st2.doOther();
// 空引用
st2 = null;
// 空引用调用实例方法会出现什么问题?空指针异常。
st2.doOther();
}
// 静态方法(静态方法不需要new对象,直接使用“类名.”来访问)
// 但是也可以使用“引用.”来访问,不建议用。(因为其他程序员会感到困惑。)
public static void doSome(){
System.out.println("静态方法doSome()执行了!");
}

// 实例方法(实例相关的都需要new对象,使用"引用."来访问。)
public void doOther(){
System.out.println("实例方法doOther执行了!");
}
}

静态和实例在具体使用场景的选择

与所在的类绑定不更改的使用静态,随new出来的实例变化的使用实例。只有在实例方法中才可以直接访问实例变量。

静态代码块

static静态代码块在类加载时执行。提供类加载时机。并且只执行一次。使用static关键字可以定义静态代码块,语法:

1
2
3
4
static {
java语句;
java语句;
}

静态代码块在类加载时执行,并且在main方法执行之前执行。静态代码块和静态变量都在类加载的时候按照自上而下的顺序执行。

实例语句块

Instance实例语句在类加载时不执行,在每次构造方法执行之前,自动执行“实例语句块”中的代码,提供对象构建时机。语法:

1
2
3
4
5
{
java语句;
java语句;
java语句;
}

this

this是一个关键字,全部小写。this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身。所以,this代表的就是“当前对象”。this存储在堆内存当中对象的内部。静态方法中不存在当前对象,所以this不能使用在静态方法中。“this.”大部分情况下可以省略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student{
// 实例变量,怎么访问?必须先new对象,通过“引用.”来访问。
String name = "zhangsan";
// 静态方法
public static void m1(){
以下报错
System.out.println(name);
// this代表的是当前对象。
ystem.out.println(this.name);

除非new
Student s = new Student();
System.out.println(s.name);

}
}

不能省略“this.”的情况

为了区分局部变量和实例变量时,this. 不能省略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 学生类
class Student{
//学号
private int no;
//姓名
private String name;
//构造方法无参
public Student(){
}
//构造方法有参
public Student(int i, String s){
no = i;
name = s;
}
上面的构造方法增强可读性
public Student(int no, String name){
this.no = no;
this.name = name;
//no是局部变量。
//this.no 是指的实例变量。
// this. 的作用是:区分局部变量和实例变量,避免就近原则产生误解。
}

this()

this除了可以使用在实例方法中,还可以用在构造方法中。通过当前的构造方法去调用另一个本类的构造方法以做到代码复用,可以使用以下语法格式:

1
this(实际参数列表);

注意:

对于this()的调用只能出现在构造方法的第一行。

使用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Date{ // 代码封装,属性私有化,对外提供setter and getter
private int year;
private int month;
private int day;
// 构造方法无参
// 调用无参数构造方法,初始化的日期是固定值。
public Date(){
/*
this.year = 1970;
this.month = 1;
this.day = 1;
*/
this(1970, 1, 1);
}
// 构造方法有参数
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}

继承(Inheritance)

作用

​ 基本作用:子类继承父类,代码可以得到复用。(理解为节省储存的复制,故不能理解为调用)
​ 主要(重要)作用:有了继承关系,才有了方法覆盖和多态机制。

相关特性

  • B类继承A类,则称A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、派生类、扩展类。语法:
1
2
class A{}
class B extends A{}
  • java 中的继承只支持单继承,不支持多继承,C++中支持多继承,换句话说,java 中不允许这样写代码:
1
class B extends A,C{ } 
  • java 中不支持多继承,但有的时候会产生间接继承的效果。例如:class C extends B,class B extends A,则C 直接继承 B,间接继承 A。

  • 子类继承父类,除构造方法不能继承之外,剩下都可以继承。但是私有的属性无法在子类中直接访问(父类中private修饰的不能在子类中直接访问。可以通过间接的手段来访问)。

  • 所有类则默认继承 Object类,Object类是 java 语言提供的根类,也就是说,一个对象与生俱来就有 Object类型中所有的特征。

  • 继承也存在缺点,类继承导致耦合度高。

使用条件

属性有交集时可以使用。

方法覆盖和多态(Polymorphism)

方法覆盖

子类继承父类时,当继承的方法无法满足当前子类的需求时,子类有权利对这个方法进行重新编写,叫“方法的覆盖”。
方法覆盖又叫做:方法重写(重新编写),英语:Override、Overwrite。
比较常见的:方法覆盖、方法重写、override

条件

  • 两个类必须要有继承关系。
  • 重写之后的方法和之前的方法具有:
                    相同的返回值类型、
                          相同的方法名、
                          相同的形式参数列表。
    
  • 访问权限不能更低,可以更高。
  • 重写之后的方法不能比之前的方法抛出更多的异常,可以更少。

注意:

  • 方法覆盖只是针对于方法,和属性无关。
  • 私有方法无法覆盖。
  • 构造方法不能被继承,所以构造方法也不能被覆盖。
  • 方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。
  • 重写之后的方法和之前的方法具有的返回值类型如果是基本数据类型,则必须一致。如果是引用数据类型,重写之后返回值类型可以变的更小(但实际开发中基本不用)。

使用例:覆盖Object类中的toString()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class OverrideTest04{
public static void main(String[] args){
// 创建一个日期对象
MyDate t1 = new MyDate();
// 调用toString()方法(将对象转换成字符串形式。)
// 希望输出:xxxx年xx月xx日
System.out.println(t1.toString());
重写MyDate的toString()方法之前的结果/MyDate@28a418fc
当输出一个引用的时候,println方法会自动调用引用的toString方法。
MyDate t2 = new MyDate(2008, 8, 8);
System.out.println(t2); //2008年8月8日
}
}
// 日期类
class MyDate {
private int year;
private int month;
private int day;
public MyDate(){
this(1970,1,1);
}
public MyDate(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public void setYear(int year){
this.year = year;
}
public int getYear(){
return year;
}
public void setMonth(int month){
this.month = month;
}
public int getMonth(){
return month;
}
public void setDay(int day){
this.day = day;
}
public int getDay(){
return day;
}

// 从Object类中继承过来的那个toString()方法已经无法满足我业务需求了。
// 我在子类MyDate中有必要对父类的toString()方法进行覆盖/重写。
// 我的业务要求是:调用toString()方法进行字符串转换的时候,
// 希望转换的结果是:xxxx年xx月xx日,这种格式。
// 重写一定要复制粘贴,不要手动编写,会错的。
public String toString() {
return year + "年" + month + "月" + day + "日";
}
}

多态

多态表示多种形态:编译的时候一种形态,运行的时候另一种形态。

静态绑定发生在编译阶段,绑定父类的方法。动态绑定发生在运行阶段,动态绑定子类型对象的方法。

向上转型

​ 子 —> 父(自动类型转换)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test01{

public static void main(String[] args){
//向上转型:
Animal a2 = new Cat();
Animal a3 = new Bird();
// 调用a2的move()方法
a2.move();//cat走猫步!
/*
分析a2.move();:
java程序分为编译阶段和运行阶段。
先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal,
所以编译器在检查语法的时候,会去Animal.class
字节码文件中找move()方法,找到了,绑定上move()
方法,编译通过,静态绑定成功。(编译阶段属于静态绑定。)
再来分析运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象是
Cat对象,所以move的时候,真正参与move的对象是一只猫,
所以运行阶段会动态执行Cat对象的move()方法。这个过程
属于运行阶段绑定。(运行阶段绑定属于动态绑定。)

向下转型

​ 父 —> 子(强制类型转换,需要加强制类型转换符)

什么时候必须使用“向下转型”?
不要随便做强制类型转换。
当你需要访问的是子类对象中“特有”的方法。此时必须进行向下转型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// ======================================================================
Animal a5 = new Cat(); // 底层对象是一只猫。
a5.catchMouse();
// 错误: 找不到符号
// 分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定。
// 只有编译通过的代码才能运行。没有编译,根本轮不到运行。
// 编译器只知道a5的类型是Animal,去Animal.class文件中找catchMouse()方法
// 结果没有找到,所以静态绑定失败,编译报错。无法运行。(语法不合法。)

// 假设代码写到了这里,我非要调用catchMouse()方法怎么办?
// 这个时候就必须使用“向下转型”了。(强制类型转换)
Cat x = (Cat)a5;
x.catchMouse(); //猫正在抓老鼠!!!!
// 以下这行代码为啥没报错????
// 因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系。所以没报错。

instanceof:避免向下转型的风险
1
2
3
4
5
6
7
8
9
10
Animal a6 = new Bird(); //表面上a6是一个Animal,运行的时候实际上是一只鸟儿。
Cat y = (Cat)a6;
y.catchMouse();
/*
Bird和Cat之间没有继承关系。
运行是出现异常,这个异常和空指针异常一样非常重要,也非常经典:
java.lang.ClassCastException:类型转换异常。
java.lang.NullPointerException:空指针异常。
*/

避免ClassCastException异常的发生

java规范中要求,对类型进行向下转型时,一定要使用运算符:instanceof (运行阶段动态判断)

  • instanceof可以在运行阶段动态判断引用指向的对象的类型。
  • instanceof的语法:
1
引用 instanceof 类型
  • instanceof运算符的运算结果只能是:true/false
  • c是一个引用,c变量保存了内存地址指向了堆中的对象。
    (c instanceof Cat)为true:
                    c引用指向的堆内存中的java对象是一个Cat。
    
    (c instanceof Cat)为false:
                    c引用指向的堆内存中的java对象不是一个Cat。
    
1
2
3
4
5
6
7
		System.out.println(a6 instanceof Cat); //false
if(a6 instanceof Cat){ // 如果a6是一只Cat
Cat y = (Cat)a6; // 再进行强制类型转换
y.catchMouse();
}
}
}

以下写在其他文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 动物类:父类
public class Animal{

// 移动的方法
public void move(){
System.out.println("动物在移动!!!");
}

}

// 鸟儿类,子类
public class Bird extends Animal{

// 重写父类的move方法
public void move(){
System.out.println("鸟儿在飞翔!!!");
}

// 也有自己特有的方法
public void sing(){
System.out.println("鸟儿在歌唱!!!");
}

}

// 猫类,子类
public class Cat extends Animal{

// 对move方法进行重写
public void move(){
System.out.println("cat走猫步!");
}

// 猫除了move之外,应该有自己特有的行为,例如抓老鼠。
// 这个行为是子类型对象特有的方法。
public void catchMouse(){
System.out.println("猫正在抓老鼠!!!!");
}

}

super

特点

​ super与this有许多共同点。(可理解为特殊的this,作用于子类继承的父类特征)

  • super能出现在实例方法和构造方法中。
  • super的语法是:“super.”、“super()”
  • super不能使用在静态方法中。
  • super. 大部分情况下是可以省略的。
  • super.什么时候不能省略呢? ???????
  • super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。

注意:

super 不是引用。super也不保存内存地址,super也不指向任何对象。super 只是代表当前对象内部的那一块父类型的特征。

使用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SuperTest06 {
// 实例方法
public void doSome(){
// SuperTest06@2f92e0f4
System.out.println(this);
// 输出“引用”的时候,会自动调用引用的toString()方法。
//System.out.println(this.toString());

//编译错误: 需要'.'
//System.out.println(super);
}
// 静态方法,主方法
public static void main(String[] args){
SuperTest06 st = new SuperTest06();
st.doSome();
}
}

super()

  • 调用父类的构造方法。当一个构造方法第一行既没有this()又没有super()时,默认存在一个super(),表示通过当前子类的构造方法调用父类的无参数构造方法。所以必须保证父类的无参数构造方法是存在的。

  • this()和super() 不能共存。

  • 在java语言中不管new什么对象,Object类的无参数构造方法一定会执行。(Object类的无参数构造方法是处于“栈顶部”)

使用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//输出顺序:13654
public class SuperTest02{
public static void main(String[] args){
new C();
}
}
class A extends Object{
public A(){
System.out.println("1"); //1
}
}

class B extends A{
public B(){
System.out.println("2"); //2
}
public B(String name){
super();
System.out.println("3"); // 3
}
}

class C extends B{
public C(){ // 这个是最先调用的。但是最后结束。
this("zhangsan");
System.out.println("4");//4
}
public C(String name){
this(name, 20);
System.out.println("5");//5
}
public C(String name, int age){
super(name);
System.out.println("6");//6
}
}

注意:
写代码时,应手动写出一个类的无参数构造方法,否则可能会影响到“子类对象的构建”。

super.

访问父类的属性:            super.属性名
访问父类的方法:            super.方法名(实参)

​ super. 大部分情况下可以省略。当父类和子类中有同名属性,有同样的方法,想在子类中访问父类的,super. 不能省略。

1
2
访问当前对象的name属性:				  	  this.name
访问当前对象的父类型特征中的name属性: super.name

使用例1:访问父类的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class SuperTest05{
public static void main(String[] args){
Vip v = new Vip("张三");
v.shopping();
}
}
class Customer {
String name;
public Customer(){}
public Customer(String name){
super();
this.name = name;
}
public void doSome(){
System.out.println(this.name + " do some!");
System.out.println(name + " do some!");
//错误: 找不到符号
//System.out.println(super.name + " do some!");
}
}
class Vip extends Customer{

// 假设子类也有一个同名属性
// java中允许在子类中出现和父类一样的同名变量/同名属性。
String name; // 实例变量
public Vip(){
}
public Vip(String name){
super(name);
// 默认存在this.name = null;
}
1
2
3
4
5
public void shopping(){
System.out.println(this.name + "正在购物!"); // null 正在购物
System.out.println(super.name + "正在购物!"); // 张三正在购物
System.out.println(name + "正在购物!"); // null 正在购物
}

使用例2:访问父类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
		/*	输出:
Cat move!
Cat move!
Animal move!
*/
public class SuperTest07{
public static void main(String[] args){

Cat c = new Cat();
c.yiDong();
}
}
class Animal{
public void move(){
System.out.println("Animal move!");
}
}
class Cat extends Animal{
// 对move进行重写。
public void move(){
System.out.println("Cat move!");
}
// 单独编写一个子类特有的方法。
public void yiDong(){
this.move();
move();
// super. 不仅可以访问属性,也可以访问方法。
super.move();
}
}