Java面向对象基础

面向对象

学习内容

  1. 面向对象思想

  2. 类与对象及其使用

  3. 对象的内存图

  4. 成员变量和局部变量的区别

  5. 匿名对象

  6. 封装(private)

  7. this关键字

  8. 构造方法

  9. static关键字

  10. l 继承

  11. l 多态

  12. l 抽象类

  13. l 接口

  14. l 包和导包

  15. l 权限修饰符

  16. l 内部类


一、面向对象思想

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

 

面向过程思想概述

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

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

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

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

 

面向对象思想概述

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

面向对象思想概述

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

面向对象思想特点

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

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

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

    •   角色发生了转换

 

面向对象思想举例

面向对象思想举例

•    买电脑

•    吃饭

•    洗衣服

•    万事万物皆对象

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

•    把大象装进冰箱

 

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

面向对象开发

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

面向对象设计

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

面向对象特征

•    封装(encapsulation)

•    继承(inheritance)

•    多态(polymorphism)


二、类与对象关系

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

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

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

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

•    举例:学生事物

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

 

类与对象关系

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

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

举例:

•      学生

•    对象   班长就是一个对象

 

类与对象(图例)

类与对象的关系如图

 

 

类的定义

现实世界的事物

•    属性   人的身高,体重等

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

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

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

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

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

 

类与对象案例

学生类

•    如何定义

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

•    如何使用

•   创建对象:

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

•   对象名.成员变量

•   对象名.成员方法


三、对象内存图

1个对象的内存图

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

2个对象的内存图

•    方法的共用

3个对象的内存图

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


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

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的地址给到了这里
	}
}

五、匿名对象

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

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

匿名对象的两种使用情况

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

•    作为实际参数传递

/*
	匿名对象:就是没有名字的对象。
	
	匿名对象的应用场景:
		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());
 	}
}

六、封装

封装概述

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

好处:

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

•     提高了代码的复用性

•     提高安全性。

封装原则:

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

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

/*
	定义一个学生类:
		成员变量: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关键字

private关键字:

•     是一个权限修饰符。

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

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

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关键字

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

记住:

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

什么时候使用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());
	}
}

八、构造方法

构造方法作用概述

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

构造方法格式

•    方法名与类名相同

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

•    没有具体的返回值

| 思考题

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

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

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

构造方法注意事项

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

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

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

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

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

/*
	我们一直在使用构造方法,但是,我们确没有定义构造方法,用的是哪里来的呢?
	
	构造方法的注意事项:
		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();
	}
}

 

类的成员方法

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

方法具体划分:

•    根据返回值

•   有明确返回值方法

•   返回void类型的方法

•    根据形式参数

•   无参方法

•   带参方法

 

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

•    成员变量

•    构造方法

•   无参构造方法

•   带参构造方法

•    成员方法

•   getXxx()

•   setXxx()

给成员变量赋值的方式

l  无参构造方法+setXxx()

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());
    }
}

 

注意

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

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

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

 

类的初始化过程

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

•    加载Student.class文件进内存

•    在栈内存为s开辟空间

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

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

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

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

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

 

面向对象练习

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

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

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

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

 


九、static关键字

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

static关键字特点

•     随着类的加载而加载

•     优先于对象存在

•    被类的所有对象共享

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

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

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  调用不同

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

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

 

main方法是静态的

public static voidmain(String[] args) {}

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

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

•    voidjvm调用,不需要给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]);
		}
	}
}

 

制作帮助文档

制作工具类

•    ArrayTools

制作帮助文档(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

Math类概述

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

Math类特点

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

Math类讲解一个方法

•    获取随机数

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

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

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

案例:

•    猜数字小游戏

 

代码块

代码块

•    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);
	}
}

进阶内容

继承

多态

抽象类

接口

包和导包

权限修饰符

内部类


十、继承

继承概述

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

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

      •   class 子类名  extends 父类名{} 

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

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

 

继承的好处

继承的好处

•    提高了代码的复用性

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

•    提高了代码的维护性

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

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

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

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

  • 原则:低耦合,高内聚。

  • 耦合:类与类的关系

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

 

Java中继承的特点

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

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

•    class SubDemo extendsDemo{} //ok

•    class SubDemo extendsDemo1,Demo2...//error

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

•    class A{}

•    class B extends A{}

•    class C extends B{}

 

Java中继承的注意事项

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

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

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

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

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

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

 

继承中成员变量的关系

案例演示

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

结论:

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

•   首先在子类局部范围找

•   然后在子类成员范围找

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

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

 

super关键字

super的用法和this很像

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

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

用法(thissuper均可如下使用)

  • 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();
	}
}

 

继承中构造方法的关系

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

为什么呢?

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

•     每一个构造方法的第一条语句默认都是: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("林青霞");
	}
}

 

继承中构造方法的关系

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

•    子类通过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("林青霞");
	}
}

 

继承中成员方法的关系

案例演示

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

结论:

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

•   首先在子类中找

•   然后在父类中找

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

 

继承中成员方法的关系

方法重写概述

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

•    使用特点:

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

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

方法重写的应用:

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

*
	方法重写:子类中出现了和父类中方法声明一模一样的方法。
	
	方法重载:
		本类中出现的方法名一样,参数列表不同的方法。与返回值无关。

	子类对象调用方法的时候:
		先找子类本身,再找父类。
		
	方法重写的应用:
		当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。
		这样,即沿袭了父类的功能,又定义了子类特有的内容。
		
	案例:
		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("林青霞");
	}
}

 

继承中成员方法的关系

方法重写的注意事项

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

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

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

/*
	方法重写的注意事项
		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();
	}
}

 

两个面试题

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

•    Overload:方法重载 

•    Override:方法重写

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

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

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

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关键字

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();
	}
}

 

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);
	}
}

 


十一、多态

多态概述

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

•    举例:

•   猫可以是猫的类型。猫 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  所以前面静态方法不能算方法的重写

 

多态的好处和弊端

多态的好处

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

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

多态的弊端

•    不能访问子类特有功能

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

•   多态中的转型

 

多态中的转型问题

向上转型

•    从子到父

•    父类引用指向子类对象

向下转型

•    从父到子

•    父类引用转为子类对象

/*
	多态的弊端:
		不能使用子类的特有功能。
		
	我就想使用子类的特有功能?行不行?
		行。
		
	怎么用呢?
		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();
	}
}

十二、抽象类

抽象类概述

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

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

 

抽象类概述

抽象类概述

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

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

 

抽象类特点

抽象类特点

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

•   格式

•   abstract class 类名{}

•   public abstract void  eat();

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

•    抽象类不能实例化

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

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

•    抽象类的子类

•   要么是抽象类

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

 

抽象类的成员特点

成员变量

•    可以是变量

•    也可以是常量

构造方法

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

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

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

成员方法

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

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

 

抽象类练习

猫狗案例

•     具体事物:猫,狗

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

老师案例

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

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

学生案例

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

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

员工案例(备注部分)

 

抽象类的几个小问题

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

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();
	}
}

 


十三、接口

接口概述

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

 

接口特点

接口特点

•    接口用关键字interface表示

•   格式:interface 接口名{}

•    类实现接口用implements表示

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

•    接口不能实例化

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

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

•    接口的子类

•   要么是抽象类

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

 

接口成员特点

成员变量

•    只能是常量

•    默认修饰符public static final

构造方法

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

成员方法

•    只能是抽象方法

•    默认修饰符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("--------------");
	}
}

 

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

类与类

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

类与接口

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

接口与接口

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

/*
	类与类:
		继承关系,只能单继承,可以多层继承。
	类与接口:
		实现关系,可以单实现,也可以多实现。
		并且还可以在继承一个类的同时实现多个接口。
	接口与接口:
		继承关系,可以单继承,也可以多继承。
*/
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();
	}
}

 

抽象类和接口的区别

成员区别

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

•    接口常量;抽象方法

关系区别

•    类与类继承,单继承

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

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

设计理念区别

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

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

 

接口练习

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

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

/*
	猫狗案例,加入跳高的额外功能
	
	分析:从具体到抽象
		猫:
			姓名,年龄
			吃饭,睡觉
		狗:
			姓名,年龄
			吃饭,睡觉
			
		由于有共性功能,所以,我们抽取出一个父类:
		动物:
			姓名,年龄
			吃饭();
			睡觉(){}
			
		猫:继承自动物
		狗:继承自动物
		
		跳高的额外功能是一个新的扩展功能,所以我们要定义一个接口
		接口:
			跳高
			
		部分猫:实现跳高
		部分狗:实现跳高
	实现;
		从抽象到具体
		
	使用:
		使用具体类
*/
//定义跳高接口
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();
		
		//定义跳高狗并进行测试的事情自己完成。
	}
}

 

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

形式参数

•    基本类型

•    引用类型

/*
	形式参数:
		基本类型(太简单)
		引用类型
			类名:(匿名对象的时候其实我们已经讲过了)需要的是该类的对象
			抽象类:需要的是该抽象的类子类对象
			接口
*/
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(p);
	}
}

返回值类型

•    基本类型

•    引用类型

/*
	返回值类型
		基本类型:(基本类型太简单)
		引用类型:
			类:返回的是该类的对象
			抽象类:返回的是该抽象类的子类对象
			接口:返回的是该接口的实现类的对象
*/
//定义一个爱好的接口
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();
	}
}

链式编程

  • 对象.方法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();
	}
}

十四、包

包的概述

•    其实就是文件夹

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

  • A:区分同名的类

  • B:对类进行分类管理

  • a:按照功能分

  • b:按照模块分

•    包的划分:

•   举例:

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

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

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

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

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

 

包的定义及注意事项

定义包的格式

•    package 包名;

•   多级包用.分开即可

•    注意事项:

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

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

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

 

带包的类的编译和运行

手动式

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

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

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

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

•   java cn.itcast.HelloWorld

自动式

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

•   javac -d .HelloWorld.java

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

 

不同包下类之间的访问

定义两个类:Demo,Test

•    Demo

•   求和方法(sum)

•    Test

•    测试方法(main)

 

导包

导包概述

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

导包格式

•    import 包名;

•    注意:

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

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

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  类:

•     默认,publicfinalabstract

•     我们自己定义: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(){}
}

十六、内部类

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

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

内部类的访问特点:

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

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

/*
	内部类概述:
		把类定义在其他类的内部,这个类就被称为内部类。
		举例:在类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) {
	
	}
}

 

内部类位置

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

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

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

/*
	内部类位置
		成员位置:在成员位置定义的类,被称为成员内部类。	
		局部位置:在局部位置定义的类,被称为局部内部类。
		
		
	成员位置:在成员位置定义的类,被称为成员内部类。	
		
*/
class Outer {
	private int num = 10;

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

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

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

 

成员内部类

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();
	}
}

 

成员内部类

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

•    举例讲解这个问题:BodyHeart,电脑和CPU

成员内部的常见修饰符

•     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();
	}
}

 

成员内部类面试题

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

      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(???);

              }

         }

      }

在控制分别输出:302010
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);

 

局部内部类

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

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

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

•    必须被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();
	}
}

 

匿名内部类

就是内部类的简化写法。

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

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

格式:

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

本质:

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

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

	前提:存在一个类或者接口
		这里的类可以是具体类也可以是抽象类。
	
	格式:
		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();
	}
}

 

匿名内部类案例

写案例,并测试

•    如何调用方法

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

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

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

 

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

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

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

 

匿名内部类面试题

按照要求,补齐代码

     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()方法的返回值类型是一个接口。
		*/
	}
}

 

 

展开阅读全文

没有更多推荐了,返回首页