Java面向对象

面向对象

学习内容

l 面向对象思想

l 类与对象及其使用

l 对象的内存图

l 成员变量和局部变量的区别

l 匿名对象

l 封装(private)

l this关键字

l 构造方法

l static关键字

l 继承

l 多态

l 抽象类

l 接口

l 包和导包

l 权限修饰符

l 内部类

一、面向对象思想

l 前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式。

面向过程思想概述

l 我们来回想一下,这几天我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。

l 在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。

l 那么什么是面向过程开发呢?面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。

l 面向过程的代表语言:C语言

面向对象思想概述

l 当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始思索,能不能把这些步骤和功能在进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。接下来我们看看面向对象到底是什么?

l 面向对象思想概述

• 面向对象是基于面向过程的编程思想

l 面向对象思想特点

• 是一种更符合我们思想习惯的思想

• 可以将复杂的事情简单化

• 将我们从执行者变成了指挥者

•   角色发生了转换

面向对象思想举例

l 面向对象思想举例

• 买电脑

• 吃饭

• 洗衣服

• 万事万物皆对象

l 面向过程思想和面向对象思想的代码举例

• 把大象装进冰箱

面向对象开发,设计,特征

l 面向对象开发

• 就是不断的创建对象,使用对象,指挥对象做事情。

l 面向对象设计

• 其实就是在管理和维护对象之间的关系。

l 面向对象特征

• 封装(encapsulation)

• 继承(inheritance)

• 多态(polymorphism)

二、类与对象关系

l 我们学习编程语言,就是为了模拟现实世界的事物,实现信息化。比如:去超市买东西的计费系统,去银行办业务的系统。

l 我们如何表示一个现实世界事物呢:

• 属性 就是该事物的描述信息

• 行为 就是该事物能够做什么

• 举例:学生事物

l 我们学习的Java语言最基本单位是类,所以,我们就应该把事物用一个类来体现。

类与对象关系

l 类:是一组相关的属性和行为的集合

l 对象:是该类事物的具体体现

l 举例:

• 类 学生

• 对象 班长就是一个对象

类与对象(图例)

l 类与对象的关系如图

类的定义

l 现实世界的事物

• 属性 人的身高,体重等

• 行为 人可以学习,吃饭等

l Java中用class描述事物也是如此

• 成员变量就是事物的属性

• 成员方法就是事物的行为

l 定义类其实就是定义类的成员(成员变量和成员方法)

类与对象案例

l 学生类

• 如何定义

• 按照事物到类的过程一步步分析

• 如何使用

• 创建对象:

• 类名 对象名= new 类名();

• 对象名.成员变量

• 对象名.成员方法

三、对象内存图

l 1个对象的内存图

• 一个对象的基本初始化过程

l 2个对象的内存图

• 方法的共用

l 3个对象的内存图

• 其中有两个引用指向同一个对象

四、成员变量和局部变量的区别

l 在类中的位置不同

• 成员变量:类中方法外

• 局部变量:方法内或者方法声明上

l 在内存中的位置不同

• 成员变量:堆内存

• 局部变量:栈内存

l 生命周期不同

• 成员变量随着对象的存在而存在,随着对象的消失而消失

• 局部变量随着方法的调用而存在,随着方法的调用完毕而消失

l 初始化值不同

• 成员变量有默认的初始化值

• 局部变量没有默认的初始化值,必须先定义,赋值,才能使用。

形式参数问题

l 基本类型作为形式参数

l 引用类型作为形式参数

类作为形式参数的问题

如果你看到一个方法需要的参数是一个类名,就应该知道这里实际需要的是一个具体的对象。

/*
形式参数的问题:
基本类型:形式参数的改变不影响实际参数
引用类型:形式参数的改变直接影响实际参数
*/
//形式参数是基本类型
class Demo {
public int sum(int a,int b) {
return a + b;
}
}

//形式参数是引用类型
class Student {
public void show() {
System.out.println(“我爱学习”);
}
}

class StudentDemo {
//如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象。
public void method(Student s) { //调用的时候,把main方法中的s的地址传递到了这里 Student s = new Student();
s.show();
}
}

class ArgsTest {
public static void main(String[] args) {
//形式参数是基本类型的调用
Demo d = new Demo();
int result = d.sum(10,20);
System.out.println(“result:”+result);
System.out.println("--------------");

	//形式参数是引用类型的调用
	//需求:我要调用StudentDemo类中的method()方法
	StudentDemo sd = new StudentDemo();
	//创建学生对象
	Student s = new Student();
	sd.method(s); //把s的地址给到了这里
}

}
五、匿名对象

l 匿名对象:就是没有名字的对象。

• 是对象的一种简化表示形式

l 匿名对象的两种使用情况

• 对象调用方法仅仅一次的时候

• 作为实际参数传递

/*
匿名对象:就是没有名字的对象。

匿名对象的应用场景:
	A:调用方法,仅仅只调用一次的时候。
		注意:调用多次的时候,不适合。
		那么,这种匿名调用有什么好处吗?
			有,匿名对象调用完毕就是垃圾。可以被垃圾回收器回收。
	B:匿名对象可以作为实际参数传递

*/
class Student {
public void show() {
System.out.println(“我爱学习”);
}
}

class StudentDemo {
public void method(Student s) {
s.show();
}
}

class NoNameDemo {
public static void main(String[] args) {
//带名字的调用
Student s = new Student();
s.show();
s.show();
System.out.println("--------------");

	//匿名对象
	//new Student();
	//匿名对象调用方法
	new Student().show();
	new Student().show(); //这里其实是重新创建了一个新的对象
	System.out.println("--------------");
	
	
	//匿名对象作为实际参数传递
	StudentDemo sd = new StudentDemo();
	//Student ss = new Student();
	//sd.method(ss); //这里的ss是一个实际参数
	//匿名对象
	sd.method(new Student());
	
	//在来一个
	new StudentDemo().method(new Student());
}

}
六、封装

l 封装概述

• 是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

l 好处:

• 隐藏实现细节,提供公共的访问方式

• 提高了代码的复用性

• 提高安全性。

l 封装原则:

• 将不需要对外提供的内容都隐藏起来。

• 把属性隐藏,提供公共方法对其访问。

/*
定义一个学生类:
成员变量:name,age
成员方法:show()方法

我们在使用这个案例的过程中,发现了一个问题:
	通过对象去给成员变量赋值,可以赋值一些非法的数据。
	这是不合理的。
	应该是这个样子的:在赋值之前,先对数据进行判断。
	判断到底在哪里做比较合适呢?
	StudentDemo类是一个测试类,测试类一般只创建对象,调用方法。	
	所以,这个判断应该定义在Student类中。
	而我们在成员变量的位置可不可以进行数据判断呢?
	是不可以的,因为做数据校验,必须要依靠一些逻辑语句。
	逻辑语句是应该定义在方法中的,所以,我们最终决定在Student类中提供一个方法
	来对数据进行校验。

按照我们前面的分析,我们给出了一个方法进行校验。
但是呢,它偏偏不调用方法来赋值,还是直接赋值了,
这样我们的方法就没有起到作用。
我就应该要求你必须使用我的方法,而不能直接调用成员变量赋值。
怎么去强制要求不能直接使用成员变量呢?
	针对这种情况,Java就提供了一个关键字 private
	
private:私有的。可以修饰成员变量和成员方法。
	注意:被private修饰的成员只能在本类中访问。
	
其实我讲到现在讲解的是一个封装的思想。
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

*/
class Student {
//姓名
String name;
//年龄
private int age;

//写一个方法对数据进行校验
/*
	返回值类型:void
	参数列表:int a
*/
public void setAge(int a) {
	if(a < 0 || age > 120) {
		System.out.println("你给的年龄有问题");
	}else {
		age = a;
	}
}


//show()方法,显示所有成员变量值
public void show() {
	System.out.println("姓名:"+name);
	System.out.println("年龄:"+age);
}

}

class StudentDemo {
public static void main(String[] args) {
//创建学生对象
Student s = new Student();
s.show();
System.out.println("--------------");

	//给成员变量赋值
	s.name = "林青霞";
	//s.age = 27;
	s.setAge(27);
	s.show();
	System.out.println("--------------");
	
	//给age赋值
	//s.age = -27; //这个数据是不合理的
	//通过方法给值
	s.setAge(-27);
	s.show();
	System.out.println("--------------");
}

}

private关键字

l private关键字:

• 是一个权限修饰符。

• 可以修饰成员(成员变量和成员方法)

• 被private修饰的成员只在本类中才能访问。

l private最常见的应用:

• 把成员变量用private修饰

• 提供对应的getXxx()/setXxx()方法

• 一个标准的案例的使用

/*
封装和private的应用:
A:把成员变量用private修饰
B:提高对应的getXxx()和setXxx()方法
*/
//定义学生类
class Student {
//姓名
private String name;
//年龄
private int age;

//姓名获取值
public String getName() {
	return name;
}

//姓名设置值
public void setName(String n) {
	name = n;
}

//年龄获取值
public int getAge() {
	return age;
}

//年龄赋值
public void setAge(int a) {
	age = a;
}

}

//测试类
class StudentTest {
public static void main(String[] args) {
//创建学生对象
Student s = new Student();

	//使用成员变量
	//错误:被私有修饰了,外界不能直接访问了
	//System.out.println(s.name+"---"+s.age);
	System.out.println(s.getName()+"---"+s.getAge());
	
	//给成员变量赋值
	//s.name = "林青霞";
	//s.age = 27;
	//通过方法给赋值
	s.setName("林青霞");
	s.setAge(27);
	System.out.println(s.getName()+"---"+s.getAge());
}

}
七、this关键字

l this:代表所在类的对象引用

l 记住:

• 方法被哪个对象调用,this就代表那个对象

l 什么时候使用this呢?

• 局部变量隐藏成员变量

• 其他用法后面和super一起讲解

/*
标准的代码改进版

this:哪个对象调用那个方法,this就代表那个对象

*/
class Student {
private String name;
private int age;

public String getName() {
	return name; //这里其实是隐含了this
}

public void setName(String name) {
	this.name = name;
}

public int getAge() {
	return age;
}

public void setAge(int age) {
	this.age = age;
}

}

class StudentTest2 {
public static void main(String[] args) {
//创建一个对象
Student s1 = new Student();
s1.setName(“林青霞”);
s1.setAge(27);
System.out.println(s1.getName()+"—"+s1.getAge());

	//创建第二个对象
	Student s2 = new Student();
	s2.setName("刘意");
	s2.setAge(30);
	System.out.println(s2.getName()+"---"+s2.getAge());
}

}
八、构造方法

l 构造方法作用概述

• 给对象的数据进行初始化

l 构造方法格式

• 方法名与类名相同

• 没有返回值类型,连void都没有

• 没有具体的返回值

| 思考题

构造方法中可不可以有return语句呢?

可以。而是我们写成这个样子就OK了:return;

其实,在任何的void类型的方法的最后你都可以写上:return;

l 构造方法注意事项

• 如果你不提供构造方法,系统会给出默认构造方法

• 如果你提供了构造方法,系统将不再提供

如果这个时候,我们要使用无参构造方法,就必须自己给出。

推荐:永远手动自己给出无参构造方法。

• 构造方法也是可以重载的

/*
我们一直在使用构造方法,但是,我们确没有定义构造方法,用的是哪里来的呢?

构造方法的注意事项:
	A:如果我们没有给出构造方法,系统将自动提供一个无参构造方法。
	B:如果我们给出了构造方法,系统将不再提供默认的无参构造方法。
		注意:这个时候,如果我们还想使用无参构造方法,就必须自己给出。建议永远自己给出无参构造方法
	
给成员变量赋值有两种方式:
	A:setXxx()
	B:构造方法

*/

class Student {
private String name;
private int age;

public Student() {
	//System.out.println("我给了,你还给不");
	System.out.println("这是无参构造方法");
}

//构造方法的重载格式
public Student(String name) {
	System.out.println("这是带一个String类型的构造方法");
	this.name = name;
}

public Student(int age) {
	System.out.println("这是带一个int类型的构造方法");
	this.age = age;
}

public Student(String name,int age) {
	System.out.println("这是一个带多个参数的构造方法");
	this.name = name;
	this.age = age;
}

public void show() {
	System.out.println(name+"---"+age);
}

}

class ConstructDemo2 {
public static void main(String[] args) {
//创建对象
Student s = new Student();
s.show();
System.out.println("-------------");

	//创建对象2
	Student s2 = new Student("林青霞");
	s2.show();
	System.out.println("-------------");
	
	//创建对象3
	Student s3 = new Student(27);
	s3.show();
	System.out.println("-------------");
	
	//创建对象4
	Student s4 = new Student("林青霞",27);
	s4.show();
}

}

类的成员方法

l 成员方法其实就是我们前面讲过的方法

l 方法具体划分:

• 根据返回值

• 有明确返回值方法

• 返回void类型的方法

• 根据形式参数

• 无参方法

• 带参方法

一个基本类的标准代码写法

l 类

• 成员变量

• 构造方法

• 无参构造方法

• 带参构造方法

• 成员方法

• getXxx()

• setXxx()

l 给成员变量赋值的方式

l 无参构造方法+setXxx()

l 带参构造方法

一个基本类的标准代码案例

l 标准的学生类(讲解)

• 感觉调用getXxx()输出学生信息比较麻烦,加入一个方法show(),输出学生对象的成员变量信息。

class Student {
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;
}

}

//测试:
class StudentDemo {
public static void main(String[] args) {
//方式1
Student s1 = new Student();
s1.setName(“Tom”);
s1.setAge(27);
System.out.println(s1.getName()+"—"+s1.getAge());

    //方式2
    Student s2 = new Student("Jam",30);
    System.out.println(s2.getName()+"---"+s2.getAge());
}

}

l 注意

• 目前的代码是为了练习的一种标准格式

• 给成员变量赋值有两种方式,可以只写一种

• 如果不单独获取数据,可以不写getXxx()方法

类的初始化过程

l Student s = newStudent();在内存中做了哪些事情?

• 加载Student.class文件进内存

• 在栈内存为s开辟空间

• 在堆内存为学生对象开辟空间

• 对学生对象的成员变量进行默认初始化

• 对学生对象的成员变量进行显示初始化

• 通过构造方法对学生对象的成员变量赋值

• 学生对象初始化完毕,把对象地址赋值给s变量

面向对象练习

l 定义一个类Demo,其中定义一个求两个数据和的方法,定义一个测试了Test,进行测试。

l 定义一个长方形类,定义求周长和面积的方法,然后定义一个测试了Test2,进行测试。

l 定义一个员工类,自己分析出几个成员,然后给出成员变量,构造方法,getXxx()/setXxx()方法,以及一个显示所有成员信息的方法。并测试。

l 定义一个类MyMath,提供基本的加减乘除功能,然后进行测试。

九、static关键字

l 可以修饰成员变量和成员方法

l static关键字特点

• 随着类的加载而加载

• 优先于对象存在

• 被类的所有对象共享

    •    这也是我们判断是否使用静态关键字的条件

• 可以通过类名调用:既可以通过对象名调用,也可以通过类名调用,建议通过类名调用

l static关键字注意事项

• 在静态方法中是没有this关键字的

• 静态方法只能访问静态的成员变量和静态的成员方法

/*
static关键字注意事项
A:在静态方法中是没有this关键字的
如何理解呢?
静态是随着类的加载而加载,this是随着对象的创建而存在。
静态比对象先存在。
B:静态方法只能访问静态的成员变量和静态的成员方法
静态方法:
成员变量:只能访问静态变量
成员方法:只能访问静态成员方法
非静态方法:
成员变量:可以是静态的,也可以是非静态的
成员方法:可是是静态的成员方法,也可以是非静态的成员方法。
简单记:
静态只能访问静态。
*/
class Teacher {
public int num = 10;
public static int num2 = 20;

public void show() {
	System.out.println(num); //隐含的告诉你访问的是成员变量
	System.out.println(this.num); //明确的告诉你访问的是成员变量
	System.out.println(num2);
	
	//function();
	//function2();
}

public static void method() {
	//无法从静态上下文中引用非静态 变量 num
	//System.out.println(num);
	System.out.println(num2);
	
	//无法从静态上下文中引用非静态 方法 function()
	//function();
	function2();
}

public void function() {

}

public static void function2() {

}

}

class TeacherDemo {
public static void main(String[] args) {
//创建对象
Teacher t = new Teacher();
t.show();
System.out.println("------------");
t.method();
}
}

静态的内存图

l 静态的内容存在于方法区的静态区

静态变量和成员变量的区别

l 所属不同

• 静态变量属于类,所以也称为为类变量

• 成员变量属于对象,所以也称为实例变量(对象变量)

l 内存中位置不同

• 静态变量存储于方法区的静态区

• 成员变量存储于堆内存

l 内存出现时间不同

• 静态变量随着类的加载而加载,随着类的消失而消失

• 成员变量随着对象的创建而存在,随着对象的消失而消失

l 调用不同

• 静态变量可以通过类名调用,也可以通过对象调用

• 成员变量只能通过对象名调用

main方法是静态的

l public static voidmain(String[] args) {}

• public 被jvm调用,访问权限足够大。

• static 被jvm调用,不用创建对象,直接类名访问

• void被jvm调用,不需要给jvm返回值

• main 一个通用的名称,虽然不是关键字,但是被jvm识别

• String[] args以前用于接收键盘录入的

• 演示案例

/*
main方法的格式讲解:
public static void main(String[] args) {…}

	public:公共的,访问权限是最大的。由于main方法是被jvm调用,所以权限要够大。
	static:静态的,不需要创建对象,通过类名就可以。方便jvm的调用。
	void:因为我们曾经说过,方法的返回值是返回给调用者,而main方法是被jvm调用。你返回内容给jvm没有意义。
	main:是一个常见的方法入口。我见过的语言都是以main作为入口。
	String[] args:这是一个字符串数组。值去哪里了?
		这个东西到底有什么用啊?怎么给值啊?
			这个东西早期是为了接收键盘录入的数据的。
			格式是:
				java MainDemo hello world java

*/
class MainDemo {
public static void main(String[] args) {
//System.out.println(args); //[Ljava.lang.String;@175078b
//System.out.println(args.length); //0
//System.out.println(args[0]); //ArrayIndexOutOfBoundsException

	//接收数据后
	System.out.println(args); 
	System.out.println(args.length); 
	//System.out.println(args[0]); 
	for(int x=0; x<args.length; x++) {
		System.out.println(args[x]);
	}
}

}

制作帮助文档

l 制作工具类

• ArrayTools

l 制作帮助文档(API)

• javadoc -d 目录 -author -version ArrayTool.java

class ArrayTool {

//把构造方法私有,外界就不能在创建对象了
private ArrayTool(){}

public static void printArray(int[] arr) {
	for(int x=0; x<arr.length; x++) {
		if(x == arr.length-1) {
			System.out.println(arr[x]);
		}else {
			System.out.print(arr[x]+", ");
		}
	}
}

}

如何使用帮助文档

l 找到文档,打开文档

l 点击显示,找到索引,出现输入框

l 你应该知道你找谁?举例:Scanner

l 看这个类的结构(需不需要导包)

• 成员变量字段

• 构造方法构造方法

• 成员方法方法

l 看这个类的说

l 看构造方法

l 看成员方法

l 然后使用

通过API学习Math类

l Math类概述

• Math包含用于执行基本数学运算的方法

l Math类特点

• 没有构造方法,因为成员都是静态的

l Math类讲解一个方法

• 获取随机数

public static double random(): [0.0,1.0)

• 获取1-100之间的随机数

int number = (int)(Math.random()*100)+1;

l 案例:

• 猜数字小游戏

代码块

l 代码块

• 在Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)。

• 局部代码块

• 在方法中出现;限定变量生命周期,及早释放,提高内存利用率

• 构造代码块

• 在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行

• 静态代码块在类中方法外出现,加了static修饰

• 在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且值执行一次。

/*
代码块:在Java中,使用{}括起来的代码被称为代码块。
根据其位置和声明的不同,可以分为
局部代码块:局部位置,用于限定变量的生命周期。
构造代码块:在类中的成员位置,用{}括起来的代码。每次调用构造方法执行前,都会先执行构造代码块。
作用:可以把多个构造方法中的共同代码放到一起,对对象进行初始化。
静态代码块:在类中的成员位置,用{}括起来的代码,只不过它用static修饰了。
作用:一般是对类进行初始化。

面试题?
	静态代码块,构造代码块,构造方法的执行顺序?
	静态代码块 -- 构造代码块 -- 构造方法
	静态代码块:只执行一次
	构造代码块:每次调用构造方法都执行

*/
class Code {
static {
int a = 1000;
System.out.println(a);
}

//构造代码块
{
	int x = 100;
	System.out.println(x);
}

//构造方法
public Code(){
	System.out.println("code");
}

//构造方法
public Code(int a){
	System.out.println("code");
}

//构造代码块
{
	int y = 200;
	System.out.println(y);
}

//静态代码块
static {
	int b = 2000;
	System.out.println(b);
}

}

class CodeDemo {
public static void main(String[] args) {
//局部代码块
{
int x = 10;
System.out.println(x);
}
//找不到符号
//System.out.println(x);
{
int y = 20;
System.out.println(y);
}
System.out.println("---------------");

	Code c = new Code();	
	System.out.println("---------------");
	Code c2 = new Code();
	System.out.println("---------------");
	Code c3 = new Code(1);
}

}
进阶内容

l 继承

l 多态

l 抽象类

l 接口

l 包和导包

l 权限修饰符

l 内部类

十、继承

l 继承概述

• 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

• 通过extends关键字可以实现类与类的继承

  •   class 子类名  extends 父类名{} 

• 单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。

• 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。

继承的好处

l 继承的好处

• 提高了代码的复用性

• 多个类相同的成员可以放到同一个类中

• 提高了代码的维护性

• 如果功能的代码需要修改,修改一处即可

• 让类与类之间产生了关系,是多态的前提

• 其实这也是继承的一个弊端:类的耦合性很强

让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。

原则:低耦合,高内聚。

耦合:类与类的关系

内聚:自己完成某件事情的能力

Java中继承的特点

l Java只支持单继承,不支持多继承。

• 一个类只能有一个父类,不可以有多个父类。

• class SubDemo extendsDemo{} //ok

• class SubDemo extendsDemo1,Demo2…//error

l Java支持多层继承(继承体系)

• class A{}

• class B extends A{}

• class C extends B{}

Java中继承的注意事项

l 子类只能继承父类所有非私有的成员(成员方法和成员变量)

• 其实这也体现了继承的另一个弊端:打破了封装性

l 子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。

l 不要为了部分功能而去继承

l 我们到底在什么时候使用继承呢?

• 继承中类之间体现的是:”isa”的关系。

继承中成员变量的关系

l 案例演示

• 子父类中同名和不同名的成员变量

l 结论:

• 在子类方法中访问一个变量

• 首先在子类局部范围找

• 然后在子类成员范围找

• 最后在父类成员范围找(肯定不能访问到父类局部范围)

• 如果还是没有就报错。(不考虑父亲的父亲…)

super关键字

l super的用法和this很像

• this代表本类对应的引用。

• super代表父类存储空间的标识(可以理解为父类引用)

l 用法(this和super均可如下使用)

l 访问成员变量

this.成员变量 super.成员变量

l 访问构造方法(子父类的构造方法问题讲)

this(…) super(…)

l 访问成员方法(子父类的成员方法问题讲)

this.成员方法() super.成员方法()

/*
问题是:
我不仅仅要输出局部范围的num,还要输出本类成员范围的num。怎么办呢?
我还想要输出父类成员范围的num。怎么办呢?
如果有一个东西和this相似,但是可以直接访问父类的数据就好了。
恭喜你,这个关键字是存在的:super。

this和super的区别?
	分别是什么呢?
		this代表本类对应的引用。
		super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)

	怎么用呢?
		A:调用成员变量
			this.成员变量 调用本类的成员变量
			super.成员变量 调用父类的成员变量
		B:调用构造方法
			this(...)	调用本类的构造方法
			super(...)	调用父类的构造方法
		C:调用成员方法
			this.成员方法 调用本类的成员方法
			super.成员方法 调用父类的成员方法

*/
class Father {
public int num = 10;
}

class Son extends Father {
public int num = 20;

public void show() {
	int num = 30;
	System.out.println(num);
	System.out.println(this.num);
	System.out.println(super.num);
}

}

class ExtendsDemo5 {
public static void main(String[] args) {
Son s = new Son();
s.show();
}
}

继承中构造方法的关系

l 子类中所有的构造方法默认都会访问父类中空参数的构造方法

l 为什么呢?

• 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。

• 每一个构造方法的第一条语句默认都是:super()

/*
继承中构造方法的关系
A:子类中所有的构造方法默认都会访问父类中空参数的构造方法
B:为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据。
所以,子类初始化之前,一定要先完成父类数据的初始化。

		注意:子类每一个构造方法的第一条语句默认都是:super();

*/
class Father {
int age;

public Father() {
	System.out.println("Father的无参构造方法");
}

public Father(String name) {
	System.out.println("Father的带参构造方法");
}

}

class Son extends Father {
public Son() {
//super();
System.out.println(“Son的无参构造方法”);
}

public Son(String name) {
	//super();
	System.out.println("Son的带参构造方法");
}

}

class ExtendsDemo6 {
public static void main(String[] args) {
//创建对象
Son s = new Son();
System.out.println("------------");
Son s2 = new Son(“林青霞”);
}
}

继承中构造方法的关系

l 如何父类中没有构造方法,该怎么办呢?

• 子类通过super去显示调用父类其他的带参的构造方法

• 子类通过this去调用本类的其他构造方法

• 本类其他构造也必须首先访问了父类构造

• 一定要注意:

• super(…)或者this(….)必须出现在第一条语句上

• 否则,就会有父类数据的多次初始化

/*
如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢?
报错。
如何解决呢?
A:在父类中加一个无参构造方法
B:通过使用super关键字去显示的调用父类的带参构造方法
C:子类通过this去调用本类的其他构造方法
子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。

注意事项:
	this(...)或者super(...)必须出现在第一条语句上。
	如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。

/
class Father {
/

public Father() {
System.out.println(“Father的无参构造方法”);
}
*/

public Father(String name) {
	System.out.println("Father的带参构造方法");
}

}

class Son extends Father {
public Son() {
super(“随便给”);
System.out.println(“Son的无参构造方法”);
//super(“随便给”);
}

public Son(String name) {
	//super("随便给");
	this();
	System.out.println("Son的带参构造方法");
}

}

class ExtendsDemo7 {
public static void main(String[] args) {
Son s = new Son();
System.out.println("----------------");
Son ss = new Son(“林青霞”);
}
}

继承中成员方法的关系

l 案例演示

• 子父类中同名和不同名的成员方法

l 结论:

• 通过子类对象去访问一个方法

• 首先在子类中找

• 然后在父类中找

• 如果还是没有就报错。(不考虑父亲的父亲…)

继承中成员方法的关系

l 方法重写概述

• 子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。

• 使用特点:

• 如果方法名不同,就调用对应的方法

• 如果方法名相同,最终使用的是子类自己的

l 方法重写的应用:

• 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

  • 方法重写:子类中出现了和父类中方法声明一模一样的方法。

    方法重载:
    本类中出现的方法名一样,参数列表不同的方法。与返回值无关。

    子类对象调用方法的时候:
    先找子类本身,再找父类。

    方法重写的应用:
    当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。
    这样,即沿袭了父类的功能,又定义了子类特有的内容。

    案例:
    A:定义一个手机类。
    B:通过研究,我发明了一个新手机,这个手机的作用是在打完电话后,可以听天气预报。
    按照我们基本的设计,我们把代码给写出来了。
    但是呢?我们又发现新手机应该是手机,所以,它应该继承自手机。
    其实这个时候的设计,并不是最好的。
    因为手机打电话功能,是手机本身就具备的最基本的功能。
    所以,我的新手机是不用在提供这个功能的。
    但是,这个时候,打电话功能就没有了。这个不好。
    最终,还是加上这个功能。由于它继承了手机类,所以,我们就直接使用父类的功能即可。
    那么,如何使用父类的功能呢?通过super关键字调用
    */
    class Phone {
    public void call(String name) {
    System.out.println(“给”+name+“打电话”);
    }
    }

class NewPhone extends Phone {
public void call(String name) {
//System.out.println(“给”+name+“打电话”);
super.call(name);
System.out.println(“可以听天气预报了”);
}
}

class ExtendsDemo9 {
public static void main(String[] args) {
NewPhone np = new NewPhone();
np.call(“林青霞”);
}
}

继承中成员方法的关系

l 方法重写的注意事项

• 父类中私有方法不能被重写

• 子类重写父类方法时,访问权限不能更低

• 父类静态方法,子类也必须通过静态方法进行重写。(其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中会讲解)

/*
方法重写的注意事项
A:父类中私有方法不能被重写
因为父类私有方法子类根本就无法继承
B:子类重写父类方法时,访问权限不能更低
最好就一致
C:父类静态方法,子类也必须通过静态方法进行重写
其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解

	子类重写父类方法的时候,最好声明一模一样。

*/
class Father {
//private void show() {}

/*
public void show() {
	System.out.println("show Father");
}
*/

void show() {
	System.out.println("show Father");
}
/*
public static void method() {
	
}
*/

public void method() {
	
}

}

class Son extends Father {
//private void show() {}

/*
public void show() {
	System.out.println("show Son");
}
*/

public void show() {
	System.out.println("show Son");
}


public static void method() {

}

/*
public void method() {

}
*/

}

class ExtendsDemo10 {
public static void main(String[] args) {
Son s = new Son();
s.show();
}
}

两个面试题

l 方法重写和方法重载的区别?方法重载能改变返回值类型吗?

• Overload:方法重载

• Override:方法重写

方法重写:在子类中,出现和父类中一模一样的方法声明的现象。

方法重载:同一个类中,出现的方法名相同,参数列表不同的现象。

方法重载能改变返回值类型,因为它和返回值类型无关。

l this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。

/*
看程序写结果:
A:成员变量 就近原则
B:this和super的问题
this访问本类的成员
super访问父类的成员
C:子类构造方法执行前默认先执行父类的无参构造方法
D:一个类的初始化过程
成员变量进行初始化
默认初始化
显示初始化
构造方法初始化

结果:
	fu
	zi
	30
	20
	10

*/
class Fu{
public int num = 10;
public Fu(){
System.out.println(“fu”);
}
}
class Zi extends Fu{
public int num = 20;
public Zi(){
System.out.println(“zi”);
}
public void show(){
int num = 30;
System.out.println(num); //30
System.out.println(this.num); //20
System.out.println(super.num); //10
}
}
class ExtendsTest {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}

继承练习

数据初始化的面试题

A:一个类的初始化过程

B:子父类的构造执行过程

C:分层初始化

/*
看程序写结果:
A:成员变量的问题
int x = 10; //成员变量是基本类型
Student s = new Student(); //成员变量是引用类型
B:一个类的初始化过程
成员变量的初始化
默认初始化
显示初始化
构造方法初始化
C:子父类的初始化(分层初始化)
先进行父类初始化,然后进行子类初始化。

结果:
	YXYZ
	
问题:
	虽然子类中构造方法默认有一个super()
	初始化的时候,不是按照那个顺序进行的。
	而是按照分层初始化进行的。
	它仅仅表示要先初始化父类数据,再初始化子类数据。

*/
class X {
Y b = new Y();
X() {
System.out.print(“X”);
}
}

class Y {
Y() {
System.out.print(“Y”);
}
}

public class Z extends X {
Y y = new Y();
Z() {
//super
System.out.print(“Z”);
}
public static void main(String[] args) {
new Z();
}
}

final关键字

l final关键字是最终的意思,可以修饰类,成员变量,成员方法。

• 修饰类,类不能被继承

• 修饰变量,变量就变成了常量,只能被赋值一次

• 修饰方法,方法不能被重写

/*
final可以修饰类,方法,变量

特点:
	final可以修饰类,该类不能被继承。
	final可以修饰方法,该方法不能被重写。(覆盖,复写)
	final可以修饰变量,该变量不能被重新赋值。因为这个变量其实常量。
	
常量:
	A:字面值常量
		"hello",10,true
	B:自定义常量
		final int x = 10;

*/

//final class Fu //无法从最终Fu进行继承

class Fu {
public int num = 10;
public final int num2 = 20;

/*
public final void show() {

}
*/

}

class Zi extends Fu {
// Zi中的show()无法覆盖Fu中的show()
public void show() {
num = 100;
System.out.println(num);

	//无法为最终变量num2分配值
	//num2 = 200;
	System.out.println(num2);
}

}

class FinalDemo {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}

l final关键字面试题

• final修饰局部变量

• 在方法内部,该变量不可以被改变

• 在方法声明上,分别演示基本类型和引用类型作为参数的情况

• 基本类型,是值不能被改变

• 引用类型,是地址值不能被改变

/*
面试题:final修饰局部变量的问题
基本类型:基本类型的值不能发生改变。
引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
*/
class Student {
int age = 10;
}

class FinalTest {
public static void main(String[] args) {
//局部变量是基本数据类型
int x = 10;
x = 100;
System.out.println(x);
final int y = 10;
//无法为最终变量y分配值
//y = 100;
System.out.println(y);
System.out.println("--------------");

	//局部变量是引用数据类型
	Student s = new Student();
	System.out.println(s.age);
	s.age = 100;
	System.out.println(s.age);
	System.out.println("--------------");
	
	final Student ss = new Student();
	System.out.println(ss.age);
	ss.age = 100;
	System.out.println(ss.age);
	
	//重新分配内存空间
	//无法为最终变量ss分配值
	ss = new Student();
}

}
• final修饰变量的初始化时机

• 在对象构造完毕前即可

/*
final修饰变量的初始化时机
A:被final修饰的变量只能赋值一次。
B:在构造方法完毕前。(非静态的常量)
*/
class Demo {
//int num = 10;
//final int num2 = 20;

int num;
final int num2;

{
	//num2 = 10;
}

public Demo() {
	num = 100;
	//无法为最终变量num2分配值
	num2 = 200;
}

}

class FinalTest2 {
public static void main(String[] args) {
Demo d = new Demo();
System.out.println(d.num);
System.out.println(d.num2);
}
}

十一、多态

l 多态概述

• 某一个事物,在不同时刻表现出来的不同状态。

• 举例:

• 猫可以是猫的类型。猫 m = new 猫();

• 同时猫也是动物的一种,也可以把猫称为动物。

• 动物 d = new 猫();

• 在举一个例子:水在不同时刻的状态

l 多态前提和体现

• 有继承关系或者实现关系

• 有方法重写

• 有父类或者父类接口引用指向子类对象

| 多态的分类

具体类多态

class Fu {}

class Zi extends Fu {}

Fu f = new Zi();

抽象类多态

abstract class Fu {}

class Zi extends Fu {}

Fu f = new Zi();

接口多态

interface Fu {}

class Zi implements Fu {}

Fu f = new Zi();

多态案例及成员访问特点

l 多态案例

• 按照前提写一个多态的案例

l 成员访问特点

l 成员变量

l 编译看左边,运行看左边

| 构造方法

| 子类的构造都会默认访问父类构造

l 成员方法

l 编译看左边,运行看右边

l 静态方法

l 编译看左边,运行看左边

l 所以前面静态方法不能算方法的重写

多态的好处和弊端

l 多态的好处

• 提高了程序的维护性(由继承保证)

• 提高了程序的扩展性(由多态保证)

l 多态的弊端

• 不能访问子类特有功能

• 那么我们如何才能访问子类的特有功能呢?

• 多态中的转型

多态中的转型问题

l 向上转型

• 从子到父

• 父类引用指向子类对象

l 向下转型

• 从父到子

• 父类引用转为子类对象

/*
多态的弊端:
不能使用子类的特有功能。

我就想使用子类的特有功能?行不行?
	行。
	
怎么用呢?
	A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
	B:把父类的引用强制转换为子类的引用。(向下转型)
	
对象间的转型问题:
	向上转型:
		Fu f = new Zi();
	向下转型:
		Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。

*/
class Fu {
public void show() {
System.out.println(“show fu”);
}
}

class Zi extends Fu {
public void show() {
System.out.println(“show zi”);
}

public void method() {
	System.out.println("method zi");
}

}

class DuoTaiDemo4 {
public static void main(String[] args) {
//测试
Fu f = new Zi();
f.show();
//f.method();

	//创建子类对象
	//Zi z = new Zi();
	//z.show();
	//z.method();
	
	//你能够把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢?
	//如果可以,但是如下
	Zi z = (Zi)f;
	z.show();
	z.method();
}

}

多态练习

/*
看程序写结果:先判断有没有问题,如果没有,写出结果

多态的成员访问特点:
	方法:编译看左边,运行看右边。
	
继承的时候:
	子类中有和父类中一样的方法,叫重写。
	子类中没有父亲中出现过的方法,方法就被继承过来了。

/
class A {
public void show() {
show2();
}
public void show2() {
System.out.println(“我”);
}
}
class B extends A {
/

public void show() {
show2();
}
*/

public void show2() {
	System.out.println("爱");
}

}
class C extends B {
public void show() {
super.show();
}
public void show2() {
System.out.println(“你”);
}
}
public class DuoTaiTest4 {
public static void main(String[] args) {
A a = new B();
a.show();

	B b = new C();
	b.show();
}

}
十二、抽象类

l 抽象类概述

• 回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。

为什么呢?因为,我说动物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。

抽象类概述

l 抽象类概述

• 回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。

为什么呢?因为,我说动物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。

抽象类特点

l 抽象类特点

• 抽象类和抽象方法必须用abstract关键字修饰

• 格式

• abstract class 类名{}

• public abstract void eat();

• 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类

• 抽象类不能实例化

• 那么,抽象类如何实例化呢?

• 按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。

• 抽象类的子类

• 要么是抽象类

• 要么重写抽象类中的所有抽象方法

抽象类的成员特点

l 成员变量

• 可以是变量

• 也可以是常量

l 构造方法

• 有构造方法,但是不能实例化

• 那么,构造方法的作用是什么呢?

• 用于子类访问父类数据的初始化

l 成员方法

• 可以有抽象方法限定子类必须完成某些动作

• 也可以有非抽象方法提高代码服用性

抽象类练习

l 猫狗案例

• 具体事物:猫,狗

• 共性:姓名,年龄,吃饭

l 老师案例

• 具体事物:基础班老师,就业班老师

• 共性:姓名,年龄,讲课。

l 学生案例

• 具体事务:基础班学员,就业班学员

• 共性:姓名,年龄,班级,学习,吃饭

l 员工案例(备注部分)

抽象类的几个小问题

l 一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?

l abstract不能和哪些关键字共存

• private 冲突

• final 冲突

• static 无意义

/*
一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
A:可以。
B:不让创建对象。

abstract不能和哪些关键字共存?
private 冲突
final 冲突
static 无意义
*/
abstract class Fu {
//public abstract void show();
//非法的修饰符组合: abstract和private
//private abstract void show();

//非法的修饰符组合
//final abstract void show();	

//非法的修饰符组合
static abstract void show();

public static void method() {
	System.out.println("method");
}

}

class Zi extends Fu {
public void show() {}
}

class AbstractDemo3 {
public static void main(String[] args) {
Fu.method();
}
}

十三、接口

l 接口概述

• 继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了,对不。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的,对不。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被培训,只需要这部分猫狗把这些额外功能实现即可。

接口特点

l 接口特点

• 接口用关键字interface表示

• 格式:interface 接口名{}

• 类实现接口用implements表示

• 格式:class 类名 implements 接口名{}

• 接口不能实例化

• 那么,接口如何实例化呢?

• 按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。

• 接口的子类

• 要么是抽象类

• 要么重写接口中的所有抽象方法

接口成员特点

l 成员变量

• 只能是常量

• 默认修饰符public static final

l 构造方法

• 没有,因为接口主要是扩展功能的,而没有具体存在

l 成员方法

• 只能是抽象方法

• 默认修饰符public abstract

/*
接口成员特点
成员变量;只能是常量,并且是静态的。
默认修饰符:public static final
建议:自己手动给出。
构造方法:接口没有构造方法。
成员方法:只能是抽象方法。
默认修饰符:public abstract
建议:自己手动给出。

所有的类都默认继承自一个类:Object。
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。

*/
interface Inter {
public int num = 10;
public final int num2 = 20;
public static final int num3 = 30;

//错误: 需要<标识符>
//public Inter() {}

//接口方法不能带有主体
//public void show() {}

//abstract void show(); //默认public
public void show(); //默认abstract

}

//接口名+Impl这种格式是接口的实现类格式
/*
class InterImpl implements Inter {
public InterImpl() {
super();
}
}
*/

class InterImpl extends Object implements Inter {
public InterImpl() {
super();
}

public void show() {}

}

//测试类
class InterfaceDemo2 {
public static void main(String[] args) {
//创建对象
Inter i = new InterImpl();
System.out.println(i.num);
System.out.println(i.num2);
//i.num = 100;
//i.num2 = 200;
//System.out.println(i.num); //无法为最终变量num分配值
//System.out.println(i.num2);//无法为最终变量num2分配值
System.out.println(Inter.num);
System.out.println(Inter.num2);
System.out.println("--------------");
}
}

类与类,类与接口以及接口与接口的关系

l 类与类

• 继承关系,只能单继承,但是可以多层继承

l 类与接口

• 实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口

l 接口与接口

• 继承关系,可以单继承,也可以多继承

/*
类与类:
继承关系,只能单继承,可以多层继承。
类与接口:
实现关系,可以单实现,也可以多实现。
并且还可以在继承一个类的同时实现多个接口。
接口与接口:
继承关系,可以单继承,也可以多继承。
*/
interface Father {
public abstract void show();
}

interface Mother {
public abstract void show2();
}

interface Sister extends Father,Mother {

}

//class Son implements Father,Mother //多实现
class Son extends Object implements Father,Mother {
public void show() {
System.out.println(“show son”);
}

public void show2() {
	System.out.println("show2 son");
}

}

class InterfaceDemo3 {
public static void main(String[] args) {
//创建对象
Father f = new Son();
f.show();
//f.show2(); //报错

	Mother m = new Son();
	//m.show(); //报错
	m.show2();
}

}

抽象类和接口的区别

l 成员区别

• 抽象类变量,常量;有抽象方法;抽象方法,非抽象方法

• 接口常量;抽象方法

l 关系区别

• 类与类继承,单继承

• 类与接口实现,单实现,多实现

• 接口与接口继承,单继承,多继承

l 设计理念区别

• 抽象类被继承体现的是:”is a”的关系。共性功能

• 接口被实现体现的是:”like a”的关系。扩展功能

接口练习

l 猫狗案例,加入跳高的额外功能

l 老师和学生案例,加入抽烟的额外功能

/*
猫狗案例,加入跳高的额外功能

分析:从具体到抽象
	猫:
		姓名,年龄
		吃饭,睡觉
	狗:
		姓名,年龄
		吃饭,睡觉
		
	由于有共性功能,所以,我们抽取出一个父类:
	动物:
		姓名,年龄
		吃饭();
		睡觉(){}
		
	猫:继承自动物
	狗:继承自动物
	
	跳高的额外功能是一个新的扩展功能,所以我们要定义一个接口
	接口:
		跳高
		
	部分猫:实现跳高
	部分狗:实现跳高
实现;
	从抽象到具体
	
使用:
	使用具体类

*/
//定义跳高接口
interface Jumpping {
//跳高功能
public abstract void jump();
}

//定义抽象类
abstract class Animal {
//姓名
private String name;
//年龄
private int age;

public Animal() {}

public Animal(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;
}

//吃饭();
public abstract void eat();

//睡觉(){}
public void sleep() {
	System.out.println("睡觉觉了");
}

}

//具体猫类
class Cat extends Animal {
public Cat(){}

public Cat(String name,int age) {
	super(name,age);
}

public void eat() {
	System.out.println("猫吃鱼");
}

}

//具体狗类
class Dog extends Animal {
public Dog(){}

public Dog(String name,int age) {
	super(name,age);
}

public void eat() {
	System.out.println("狗吃肉");
}

}

//有跳高功能的猫
class JumpCat extends Cat implements Jumpping {
public JumpCat() {}

public JumpCat(String name,int age) {
	super(name,age);
}

public void jump() {
	System.out.println("跳高猫");
}

}

//有跳高功能的狗
class JumpDog extends Dog implements Jumpping {
public JumpDog() {}

public JumpDog(String name,int age) {
	super(name,age);
}

public void jump() {
	System.out.println("跳高狗");
}

}

class InterfaceTest {
public static void main(String[] args) {
//定义跳高猫并测试
JumpCat jc = new JumpCat();
jc.setName(“哆啦A梦”);
jc.setAge(3);
System.out.println(jc.getName()+"—"+jc.getAge());
jc.eat();
jc.sleep();
jc.jump();
System.out.println("-----------------");

	JumpCat jc2 = new JumpCat("加菲猫",2);
	System.out.println(jc2.getName()+"---"+jc2.getAge());
	jc2.eat();
	jc2.sleep();
	jc2.jump();
	
	//定义跳高狗并进行测试的事情自己完成。
}

}

形式参数和返回值问题案例

l 形式参数

• 基本类型

• 引用类型

/*
形式参数:
基本类型(太简单)
引用类型
类名:(匿名对象的时候其实我们已经讲过了)需要的是该类的对象
抽象类:需要的是该抽象的类子类对象
接口
*/
abstract class Person {
public abstract void study();
}

class PersonDemo {
public void method(Person p) {//p; p = new Student(); Person p = new Student(); //多态
p.study();
}
}

//定义一个具体的学生类
class Student extends Person {
public void study() {
System.out.println(“Good Good Study,Day Day Up”);
}
}

class PersonTest {
public static void main(String[] args) {
//目前是没有办法的使用的
//因为抽象类没有对应的具体类
//那么,我们就应该先定义一个具体类
//需求:我要使用PersonDemo类中的method()方法
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method§;
}
}
l 返回值类型

• 基本类型

• 引用类型

/*
返回值类型
基本类型:(基本类型太简单)
引用类型:
类:返回的是该类的对象
抽象类:返回的是该抽象类的子类对象
接口:返回的是该接口的实现类的对象
*/
//定义一个爱好的接口
interface Love {
public abstract void love();
}

class LoveDemo {
public Love getLove() {
//Love l = new Teacher();
//return l;

	return new Teacher();
}

}

//定义具体类实现接口
class Teacher implements Love {
public void love() {
System.out.println(“老师爱学生,爱Java,爱林青霞”);
}
}

class TeacherTest2 {
public static void main(String[] args) {
//如何测试呢?
LoveDemo ld = new LoveDemo();
Love l = ld.getLove(); //new Teacher(); Love l = new Teacher(); 多态
l.love();
}
}
l 链式编程

对象.方法1().方法2()…方法n();

这种用法:其实在方法1()调用完毕后,应该一个对象;

方法2()调用完毕后,应该返回一个对象。

方法n()调用完毕后,可能是对象,也可以不是对象。

/*
链式编程。
每次调用完毕方法后,返回的是一个对象。
*/
class Student {
public void study() {
System.out.println(“Good Good Study,Day Day Up”);
}
}

class StudentDemo {
public Student getStudent() {
return new Student();
}
}

class StudentTest3 {
public static void main(String[] args) {
//如何调用的呢?
StudentDemo sd = new StudentDemo();
//Student s = sd.getStudent();
//s.study();

	//大家注意了
	sd.getStudent().study();
}

}
十四、包

l 包的概述

• 其实就是文件夹

• 作用:对类进行分类管理

A:区分同名的类

B:对类进行分类管理

a:按照功能分

b:按照模块分

• 包的划分:

• 举例:

• 学生的增加,删除,修改,查询

• 老师的增加,删除,修改,查询

• 以及以后可能出现的其他的类的增加,删除,修改,查询

• 基本的划分:按照模块和功能分。

• 高级的划分:做项目的时候就能看到了。

包的定义及注意事项

l 定义包的格式

• package 包名;

• 多级包用.分开即可

• 注意事项:

• package语句必须是程序的第一条可执行的代码

• package语句在一个java文件中只能有一个

• 如果没有package,默认表示无包名

带包的类的编译和运行

l 手动式

• a:javac编译当前类文件。

• b:手动建立包对应的文件夹。

• c:把a步骤的class文件放到b步骤的最终文件夹下。

• d:通过java命令执行。注意了:需要带包名称的执行

• java cn.itcast.HelloWorld

l 自动式

• a:javac编译的时候带上-d即可

• javac -d .HelloWorld.java

• b:通过java命令执行。和手动式一样

不同包下类之间的访问

l 定义两个类:Demo,Test。

• Demo

• 求和方法(sum)

• Test

• 测试方法(main)

导包

l 导包概述

• 不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。

l 导包格式

• import 包名;

• 注意:

• 这种方式导入是到类的名称。

• 虽然可以最后写*,但是不建议。

l package,import,class有没有顺序关系(面试题)

package > import > class

/*
Test类,测试

导包:
	格式:import 包名;
		这种方式导入是到类的名称。
	注意:我们用谁就导谁。
	
面试题:
	package,import,class有没有顺序关系?
	有。
	package > import > class
	
	Package:只能有一个
	import:可以有多个
	class:可以有多个,以后建议是一个

*/
package cn.itcast;

import com.liuyi.Demo;

class Test {
public static void main(String[] args) {
//Demo d = new Demo();
/*
com.liuyi.Demo d = new com.liuyi.Demo();
System.out.println(d.sum(10,20));

	com.liuyi.Demo d2 = new com.liuyi.Demo();
	System.out.println(d2.sum(10,20));
	
	com.liuyi.Demo d3 = new com.liuyi.Demo();
	System.out.println(d3.sum(10,20));
	
	com.liuyi.Demo d4 = new com.liuyi.Demo();
	System.out.println(d4.sum(10,20));
	*/
	
	Demo d = new Demo();
	System.out.println(d.sum(10,20));
}

}

/*
第一个问题:找不到Demo

第二个问题:程序包com.liuyi不存在

第三个问题: Demo在com.liuyi中不是公共的; 无法从外部程序包中对其进行访问

*/
十五、权限修饰符

类及其组成可以用的修饰符

l 类:

• 默认,public,final,abstract

• 我们自己定义:public居多

l 成员变量:

• 四种权限修饰符均可,final,static

• 我们自己定义:private居多

l 构造方法:

• 四种权限修饰符均可,其他不可

• 我们自己定义:public 居多

l 成员方法:

• 四种权限修饰符均可,fianl,static,abstract

• 我们自己定义:public居多

/*
修饰符:
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract

类:
	权限修饰符:默认修饰符,public
	状态修饰符:final
	抽象修饰符:abstract
	
	用的最多的就是:public
	
成员变量:
	权限修饰符:private,默认的,protected,public
	状态修饰符:static,final
	
	用的最多的就是:private
	
构造方法:
	权限修饰符:private,默认的,protected,public
	
	用的最多的就是:public
	
成员方法:
	权限修饰符:private,默认的,protected,public
	状态修饰符:static,final
	抽象修饰符:abstract
	
	用的最多的就是:public
	
除此以外的组合规则:
	成员变量:public static final
	成员方法:public static 
	          public abstract
			  public final

*/
//此处不允许使用修饰符private
//此处不允许使用修饰符protected
//此处不允许使用修饰符static
public class Demo {
//成员变量
private int x = 10;
int y = 20;
protected int z = 30;
public int a = 40;
public final int b = 50;
public static int c = 60;
public static final int d = 70;
//此处不允许使用修饰符abstract
//abstract int e = 80;

//构造方法
private Demo(){}

Demo(String name){}

protected Demo(String name,int age) {}

public Demo(String name,int age,String address) {}

//此处不允许使用修饰符static
//public static Demo(){}
//此处不允许使用修饰符final
//public final Demo() {}
//此处不允许使用修饰符abstract
//public abstract Demo(){}

//成员方法
//static void show() {}
//abstract void show();
//final void show(){}

}
十六、内部类

l 把类定义在其他类的内部,这个类就被称为内部类。

• 举例:在类A中定义了一个类B,类B就是内部类。

l 内部类的访问特点:

• 内部类可以直接访问外部类的成员,包括私有。

• 外部类要访问内部类的成员,必须创建对象。

/*
内部类概述:
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。

内部的访问特点:
	A:内部类可以直接访问外部类的成员,包括私有。
	B:外部类要访问内部类的成员,必须创建对象。

*/
class Outer {
private int num = 10;

class Inner {
	public void show() {
		System.out.println(num);
	}
}

public void method() {
	//找不到符号
	//show();

	Inner i = new Inner();
	i.show();
}

}

class InnerClassDemo {
public static void main(String[] args) {

}

}

内部类位置

l 按照内部类在类中定义的位置不同,可以分为如下两种格式:

• 成员位置(成员内部类)

• 局部位置(局部内部类)

/*
内部类位置
成员位置:在成员位置定义的类,被称为成员内部类。
局部位置:在局部位置定义的类,被称为局部内部类。

成员位置:在成员位置定义的类,被称为成员内部类。	

*/
class Outer {
private int num = 10;

//成员位置
/*
class Inner {
	
}
*/


public void method() {
	//局部位置
	class Inner {
	
	}
}

}

class InnerClassDemo2 {
public static void main(String[] args) {

}

}

l 成员内部类

l 外界如何创建对象

l 外部类名.内部类名对象名= 外部类对象.内部类对象;

/*
成员内部类:
如何直接访问内部类的成员。
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
*/
class Outer {
private int num = 10;

class Inner {
	public void show() {
		System.out.println(num);
	}
}

}

class InnerClassDemo3 {
public static void main(String[] args) {
//需求:我要访问Inner类的show()方法
//Inner i = new Inner();
//i.show();

	//格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
	Outer.Inner oi = new Outer().new Inner();
	oi.show();
}

}

成员内部类

l 刚才我们讲解过了,成员内部类的使用,但是一般来说,在实际开发中是不会这样使用的。因为一般内部类就是不让外界直接访问的。

• 举例讲解这个问题:Body和Heart,电脑和CPU。

l 成员内部的常见修饰符

• private 为了保证数据的安全性

• static 为了让数据访问更方便

• 被静态修饰的成员内部类只能访问外部类的静态成员

• 内部类被静态修饰后的方法

• 静态方法

• 非静态方法

/*
成员内部类的修饰符:
private 为了保证数据的安全性
static 为了方便访问数据
注意:静态内部类访问的外部类数据必须用静态修饰。

案例:我有一个人(人有身体,身体内有心脏。)
	
	class Body {
		private class Heart {
			public void operator() {
				System.out.println("心脏搭桥");
			}
		}
		
		public void method() {
			if(如果你是外科医生) {
				Heart h = new Heart();
				h.operator();
			}
		}
	}
	
	按照我们刚才的讲解,来使用一下
	Body.Heart bh = new Body().new Heart();
	bh.operator();
	//加了private后,就不能被访问了,那么,怎么玩呢?
	Body b =  new Body();
	b.method();

*/
class Outer {
private int num = 10;
private static int num2 = 100;

//内部类用静态修饰是因为内部类可以看出是外部类的成员
public static class Inner {
	public void show() {
		//System.out.println(num);
		System.out.println(num2);
	}

	public static void show2() {
		//System.out.println(num);
		System.out.println(num2);
	}		
}

}

class InnerClassDemo4 {
public static void main(String[] args) {
//使用内部类
// 限定的新静态类
//Outer.Inner oi = new Outer().new Inner();
//oi.show();
//oi.show2();

	//成员内部类被静态修饰后的访问方式是:
	//格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
	Outer.Inner oi = new Outer.Inner();
	oi.show();
	oi.show2();
	
	//show2()的另一种调用方式
	Outer.Inner.show2();
}

}

成员内部类面试题

l 补齐程序(注意:内部类和外部类没有继承关系)

  class Outer {

     public int num = 10;

     class Inner {

          public int num = 20;

          public void show() {

               int num = 30;

               System.out.println(?);

               System.out.println(??);

               System.out.println(???);

          }

     }

  }

在控制分别输出:30,20,10
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);

局部内部类

l 可以直接访问外部类的成员

l 可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能

l 局部内部类访问局部变量的注意事项:

• 必须被final修饰?

• 为什么呢?

  •   因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下。

/*
局部内部类
A:可以直接访问外部类的成员
B:在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能

面试题:
	局部内部类访问局部变量的注意事项?
	A:局部内部类访问局部变量必须用final修饰
	B:为什么呢?
		局部变量是随着方法的调用而调用,随着调用完毕而消失。
		而堆内存的内容并不会立即消失。所以,我们加final修饰。
		加入final修饰后,这个变量就成了常量。既然是常量。你消失了。
		我在内存中存储的是数据20,所以,我还是有数据在使用。

*/
class Outer {
private int num = 10;

public void method() {
	//int num2 = 20;
	//final int num2 = 20;
	class Inner {
		public void show() {
			System.out.println(num);
			//从内部类中访问本地变量num2; 需要被声明为最终类型
			System.out.println(num2);//20
		}
	}
	
	//System.out.println(num2);
	
	Inner i = new Inner();
	i.show();
}

}

class InnerClassDemo5 {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}

匿名内部类

l 就是内部类的简化写法。

l 前提:存在一个类或者接口

• 这里的类可以是具体类也可以是抽象类。

l 格式:

new类名或者接口名(){重写方法;}

l 本质:

• 是一个继承了类或者实现了接口的子类匿名对象

/*
匿名内部类
就是内部类的简化写法。

前提:存在一个类或者接口
	这里的类可以是具体类也可以是抽象类。

格式:
	new 类名或者接口名(){
		重写方法;
	}
	
本质是什么呢?
	是一个继承了该类或者实现了该接口的子类匿名对象。

*/
interface Inter {
public abstract void show();
public abstract void show2();
}

class Outer {
public void method() {
//一个方法的时候
/*
new Inter() {
public void show() {
System.out.println(“show”);
}
}.show();
*/

	//二个方法的时候
	/*
	new Inter() {
		public void show() {
			System.out.println("show");
		}
		
		public void show2() {
			System.out.println("show2");
		}
	}.show();
	
	new Inter() {
		public void show() {
			System.out.println("show");
		}
		
		public void show2() {
			System.out.println("show2");
		}
	}.show2();
	*/
	
	//如果我是很多个方法,就很麻烦了
	//那么,我们有没有改进的方案呢?
	Inter i = new Inter() { //多态
		public void show() {
			System.out.println("show");
		}
		
		public void show2() {
			System.out.println("show2");
		}
	};
	
	i.show();
	i.show2();
}

}

class InnerClassDemo6 {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}

匿名内部类案例

l 写案例,并测试

• 如何调用方法

• 加入方法有多个,如何调用呢?

• 方式1:每一种格式调用一个,太麻烦

• 方式2:用类或者接口接收该子类对象,多态思想

匿名内部类在开发中的使用

l 首先回顾我们曾经讲过的方法的形式参数是引用类型的情况,重点是接口的情况,我们知道这里需要一个子类对象。而匿名内部类就是一个子类匿名对象,所以,可以使用匿名内部类改进以前的做法。

/*
匿名内部类在开发中的使用
*/
interface Person {
public abstract void study();
}

class PersonDemo {
//接口名作为形式参数
//其实这里需要的不是接口,而是该接口的实现类的对象
public void method(Person p) {
p.study();
}
}

//实现类
class Student implements Person {
public void study() {
System.out.println(“好好学习,天天向上”);
}
}

class InnerClassTest2 {
public static void main(String[] args) {
//测试
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method§;
System.out.println("--------------------");

	//匿名内部类在开发中的使用
	//匿名内部类的本质是继承类或者实现了接口的子类匿名对象
	pd.method(new Person(){
		public void study() {
			System.out.println("好好学习,天天向上");
		}
	});
}

}

匿名内部类面试题

l 按照要求,补齐代码

 interface Inter { void show(); }

 class Outer { //补齐代码}

 class OuterDemo {

    public static void main(String[] args) {

       Outer.method().show();

    }

 }

要求在控制台输出”HelloWorld”

/*
匿名内部类面试题:
按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
*/
interface Inter {
void show();
//public abstract
}

class Outer {
//补齐代码
public static Inter method() {
//子类对象 – 子类匿名对象
return new Inter() {
public void show() {
System.out.println(“HelloWorld”);
}
};
}
}

class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
/*
1:Outer.method()可以看出method()应该是Outer中的一个静态方法。
2:Outer.method().show()可以看出method()方法的返回值是一个对象。
又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。
*/
}
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值