一、面向对象——多态

1、多态概述

①什么是多态?某一事物在不同时刻表现出来的不同状态。

如:猫可以是猫的类型。猫 m = new猫();同时猫也是动物的一种,也可以把猫称为动物。

②多态的好处:提高了代码的可扩展性,前期定义的代码可以使用后期的内容。

③多态的弊端:前期定义的内容不能使用后期子类的特有功能。

④多态的前提:

  • 必须要有关系:继承,实现。
  • 有覆盖。
  • 有父类引用指向子类对象。

2、多态时成员的特点

①成员变量:编译时,参考引用型变量所属的类中是否有调用的成员变量,有则编译通过,没有则编译失败。运行时,参考引用型变量所属的类是否有调用的成员变量,并运行该所属类中的成员变量。简单的说,编译和运行都参考等号的左边。

②成员函数(非静态):编译时,参考引用型变量所属的类中是否有调用的函数,有则编译通过,没有则编译失败。运行时,参考的是对象所属的类是否有调用的函数,简单的说,编译看左边,运行看右边。

③静态函数:简单说,编译和运行都看做左边。其实对于静态方法,是不需要对象的。直接用类名调用即可。

3、多态中的转型问题

①向上转型:从子到父,父类引用指向子类对象。

②向下转型:从父到子,父类引用转为子类对象。

在多台向下转型中容易出现的问题,代码演示如下:

/*
	ClassCastException:类型转换异常
	一般在多态的向下转型中容易出现
*/
class Animal {
	public void eat(){}
}

class Dog extends Animal {
	public void eat() {}
	
	public void lookDoor() {
	
	}
}

class Cat extends Animal {
	public void eat() {
	
	}
	
	public void playGame() {
		
	}
}

class DuoTaiDemo5 {
	public static void main(String[] args) {
		//内存中的是狗
		Animal a = new Dog();
		Dog d = (Dog)a;
		
		//内存中是猫
		a = new Cat();
		Cat c = (Cat)a;
		
		//内存中是猫
		Dog dd = (Dog)a; //ClassCastException
	}
}

二、面向对象——内部类

1、内部类(inner class)概述

①概念:内部类是指定义在另一个类中的类。当描述事物时,事物的内部还有事物,该事物就用内部类来描述。因为内部事物在使用外部事物的内容。

②内部类访问的特点:

  • 内部类可以直接访问外部类中的成员。
  • 外部类需要访问内部类,必须建立内部类的对象
  • 如果内部类中有静态成员,那么外部类也必须是静态的。

2、内部类的分类

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

  • 成员位置——成员内部类
  • 局部位置——局部内部类

①成员内部类

a、外界如何创建对象?外部类名.内部类名  对象名 = 外部类对象.内部类对象;

b、成员内部类常见的修饰符

  • private 为了保证数据的安全性
  • static 为了让数据访问更方便。被静态修饰的成员内部类只能访问外部的静态成员。

内部类被静态修饰后的方法有静态和非静态之分,它们的访问和不用静态不是一样的。

访问非静态方法:外部类名.内部类名对象名 = new 外部类名.内部类名();

访问静态方法:上面创建的对象访问,或者外部类名.内部类名.方法名();

成员内部类面试题:

class Outer {
	public int num = 10;
	
	class Inner {
		public int num = 20;
		
		public void show() {
			int num = 30;
			System.out.println(num);//30
			System.out.println(this.num);//20
			System.out.println(Outer.this.num);//10
		}
	}
}

class OuterDemo {
	public static void main(String[] args) {
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	} 
}

②局部内部类

a、局部内部类可以直接访问外部类的成员。

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

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

  • 必须被final修饰。为什么?因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。

如下面的代码所示:

class Outer
{
	int x = 3;
	void method(final int a)
	{
		final int y = 4;
		//局部内部类
                class Inner
		{
			void function()
			{
				System.out.println(y);
			}
		}
		new Inner().function();//使用局部内部类中的方法。
	}
}
class  InnerClassDemo
{
	public static void main(String[] args) 
	{
		Outer out = new Outer();
		out.method(7);//打印7
		out.method(8);//打印8
	}
}

③匿名内部类

a、什么是匿名内部类?匿名内部类其实就是内部类的简写格式。

b、匿名内部类的前提:存在一个类或接口。

c、匿名内部类的格式:new 类名或者接口名(){重写方法}。

d、匿名内部类的本质:是一个继承了类或者实现了接口的子类匿名对象。

e、匿名内部类的通常使用场景之一:当函数参数是接口类型时,而且接口中的方法不超过三个,可以使用匿名内部类作为实际参数进行传递。

匿名内部类在开发中的使用代码实例:

/*
	匿名内部类在开发中的使用
*/
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”

题目分析:Outer.method.show();相当于Inter.in = Outer.method();  in.show();

                  Outer.method():Outer类中有个静态的方法method。

                  .show():method这个方法运算后的结果是一个对象。而且是一个Inter类型的对象。因为只有Inter类型的对象,才能调用show方法。

所以,补齐后的代码为:

/*
	匿名内部类面试题:
		按照要求,补齐代码
			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()方法的返回值类型是一个接口。
		*/
	}
}

三、面向对象——异常

1、异常的概述

①什么是异常?

a、异常是指在运行时期发生的不正常情况。在java中用类的形式对不正常情况进行了描述和封装,描述不正常情况的类,就是异常类。

b、在以往的正常情况,流程代码和问题处理代码相结合。现在将正常流程代码和问题处理代码相分离,提高阅读性。

c、其实异常就是java通过面向对象的思想将问题封装成了对象,用异常类对其进行描述,不同的问题用不同的类进行具体描述。

②异常的由来

在理想状况下,用户输入的数据永远是正确的,打开的文件一定存在,并且永远不会出现bug。但是,在现实中我们往往会遇到许多的问题。如果一个用户在运行程序期间,由于程序的错误或一些外部环境的影响造成数据的丢失,用户就有可能不再使用这个程序了,为了避免这类事情的发生,至少应该做到以下几点:

  • 向用户通报错误。
  • 保存所有的操作结果。
  • 允许用户以适当的形式退出程序。

2、异常体系

对于程序出现的问题,可能会有很多种,这就意味着描述的类也很多,将其共性进行向上抽取,就形成了异常体系。

异常体系(Throwable):无论是error还是异常,都应该抛出,让调用者知道并处理。该体系的优点就在于Throwable及其所有的子类都具有可抛性。可抛性是通过两个关键字来体现的,throw和throws,凡是可以被这两个关键字操作的类和对象都具有可抛性。

最终就将问题分成了两大类:

①error:一般不可处理。java运行时系统的内部错误和资源耗尽错误。是由jvn抛出的严重性问题,这种问题一般不针对性处理,直接修改程序。

②Exception:可处理的。Exception体系。

a、该体系的特点:子类的后缀都是用其父类名作为后缀,阅读性很强。

b、在java中没有定义的异常就按照java异常的创建思想,面向对象,进行自定义描述,并封装成对象。

注意:如果一个类称为异常类,则必须要继承异常体系,因为只要继承异常体系的子类才具有可抛性。才可以被两个关键字操作。

异常体系如下图所示:

Java中不同软件包中的Java类如何调用 java不同包之间的调用_java面向对象

3、异常的分类

①异常通常分为两种,一种是编译时被检测异常,一种是编译时不被检测异常(运行时异常)。

  • 编译时被检测异常:只要是Exception及其子类都是,除了RuntimeException体系。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理。
  • 编译时不检测异常(运行时异常):就是Exception中的RuntimeException及其子类。这种问题的发生,无法让功能继续,运算无法进行,更多的是因为调用者的原因导致的,或者是引发了内部状态的改变而导致的,那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强行停止,让调用者修改程序代码。“如果出现RuntimeException,那么就一定是你的问题”。

4、异常的处理

①异常处理机制

在java应用程序中,异常处理机制为:抛出异常,捕获异常。

  • 抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
  • 捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

②throws和throw的区别

  • throws使用在函数上,throw使用在函数内。
  • throws抛出的是异常类,可以抛出多个,用逗号隔开;throw抛出的是异常对象。

③在java中,提供了特有的处理异常的方式,格式如下:

try
 {
     需要被检测的代码;
 }
 catch(异常类 变量)
 {
     处理异常的代码;(处理方式)
 }
 finally
 {
     一定会执行的语句;
 }

除此之外还有两种格式,一种是没有finally代码块,一种是没有catch代码块。

注意:在finally中定义的通常是关闭资源代码,因为资源必须释放。finally只有一种情况不会执行:当系统执行到System.exit的时候。

④自定义异常

定义类继承Exception或者RuntimeException。目的有两个:一是为了让该自定义类具有可抛性,二是为了让该类具备操作异常的共性方法。

当要定义自定义异常信息时,可以使用父类已经定义好的功能。异常信息传递给父类的构造函数。代码实例如下:

class MyException extends Exception{

	MyException(String message){

		super(message);
	}
}

自定义异常:按照java面向对象的思想,将程序中出现的特殊问题进行封装。

⑤异常处理原则:

  • 函数内容如果要抛出需要检测的异常,那么函数上必须要有声明,否则必须在函数内用try  catch进行捕捉,否则编译失败。
  • 如果调用到了声明异常的函数,要么try catch,要么throws,否则编译失败。
  • 什么时候try,什么时候throws呢?功能内容可以解决就用try…catch;解决不了,用throws告诉调用者,由调用者解决。
  • 一个功能如果抛出了多个异常,那么调用时,必须有对应的多个catch进行针对性的处理,内部有几个需要检测的异常,就抛几个,抛出几个,就catch几个。

⑥异常处理注意事项

a、子类在覆盖父类方法时,父类如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类。

b、如果父类抛出了多个异常,呢吗子类只能抛出父类异常的子集。简单的说,子类覆盖父类的异常或者子类或子集。

注意:如果父类的方法没有抛出异常,那么子类的方法绝对不能抛出异常。只能进行捕获处理。

⑦异常实际应用中的应用和总结

a、在处理运行时异常时,采用逻辑去合理规避同时辅助try—catch处理。

b、在多重catch块后面。可以加上一个catch(Exception)来处理可能被一楼的异常。

c、对于不确定的代码,也可以加上try—catch处理潜在的异常。

d、尽量去处理异常,切忌只是简单的调用printstack.Trance()去打印输出。

e、具体如何处理异常,要根据不同的业务需求和异常类型来决定。

f、尽量添加finally代码块去释放占用的资源。

⑧异常综合练习代码演示:

/*
毕老师用电脑上课。

开始思考上课中出现的问题。

比如问题是
	电脑蓝屏。
	电脑冒烟。
要对问题进行描述,封装成对象。

可是当冒烟发生后,出现讲课进度无法继续。

出现了讲师的问题:课时计划无法完成。
*/
//自定义电脑蓝屏异常
class LanPingException extends Exception{
	LanPingException(String message){
		super(message);
	}
}
//自定义电脑冒烟异常
class MaoYanException extends Exception{
	MaoYanException(String message){
		super(message);
	}
}

//出现了问题,无法上课,课时计划无法完成异常
class NoPlanException extends Exception{
	NoPlanException(String msg){
		super(msg);
	}
}

class Computer{
	private int state = 3;
	//电脑启动
	public void run()throws LanPingException,MaoYanException{
		if(state==2)
			throw new LanPingException("蓝屏了");
		if(state==3)
			throw new MaoYanException("冒烟了");

		System.out.println("电脑运行");
	}
	//电脑重启
	public void reset(){
		state = 1;
		System.out.println("电脑重启");
		
	}
}

class Teacher{
	private String name;
	private Computer cmpt;
	//对老师进行初始化
	Teacher(String name){
		this.name = name;
		cmpt = new Computer();
	}
//老师讲课
	public void prelect()throws NoPlanException{
		try{
			cmpt.run();			
		}
		catch (LanPingException e){
			cmpt.reset();
		}
		catch (MaoYanException e){
			test();
			throw new NoPlanException("课时无法继续"+e.getMessage());	
		}
		System.out.println("讲课");
	}
	public void test(){
		System.out.println("练习");
	}

}

class ExceptionTest {
	public static void main(String[] args) {
		Teacher t = new Teacher("毕老师");
		try{
			t.prelect();
		}
		catch (NoPlanException e){
			
			System.out.println(e.toString());
			System.out.println("换老师或者放假");
		}
		
	}
}

四、面向对象——包(package)

1、什么是包?

包其实就是文件夹。在包中,我们通常存放的是类文件,因为我们在编写程序时,难免会出现类名相同的情况。为了方便于对类进行管理,java中就有了包的出现,在不同的包中可以有相同的类名,调用的时候连同包名一起调用即可。包也是一种封装形式。

报名的书写规则:

  • 包名必须写在程序的第一行。
  • 类文件的全称:包名.类名。

2、包的作用

①对子类文件进行分类管理。将一些相关的类放在同一个包中。

②给类提供多层命名(名称)空间。

③避免多个类重命名,对于相同的类名,可通过包名将两者区分。

④包的出现可以将java 的类文件与源文件相分离。

3、包与包之间的访问

①要访问其它包中的类,需要定义类的全称。包名.类名。

②包如果不在当前路径,需要使用classpath设定环境变量,为JVM指明路径。

③被访问的包中的类权限必须是public的。被访问的包中的类的方法也必须是public的。

4、四种权限修饰词

 

public

protected

default

private

同一类中

ok

ok

ok

ok

同一包中

ok

ok

ok

 

子类中

ok

ok

 

 

不同包中

ok

 

 

 

5、import(导包)

①导包的原则:用到哪个类,就导哪个类,不要多导。(尽管多导也能运行)

②导包的作用:

  • 可以简化书写。
  • 一个程序文件中只有一个package,但可以有多个import。

③导包的注意事项:

  • 在导包时,用到哪个写哪个,尽量不要import pack.*;一般是import pack.Demo;可以节省内存空间,也能提高点效率。
  • 定义包名不要重复。一般我们定义包名是用url的倒写形式。如:package  cn.itheima.Demo
  • 定义包名时,包名都是小写字母。