Java-面向对象(继承、封装、多态、内部类、异常)

来源:互联网 发布:海平面上升最新数据 编辑:程序博客网 时间:2024/06/01 16:49

一.定义

什么是面向对象:复杂问题 简单化,由执行者变为指挥者
面向对象的概念:
1.这是一种基于面向过程的思想
2.将功能封装进对象,强调具备了功能的对象

面向对象特点:
1.面向对象就是一种常见的思想,符合人们的思考习惯
2.面向对象的出现,将复杂的问题简单化
3.面向对象的出现,让曾经在过程中的执行者,变成了对象中的指挥者

面试题:你是怎么理解面向对象的?
1.它符合现在人们思考的一种习惯
2.它让复杂的事情简单化
3.它让我们从曾经的执行者变为了现在的指挥者

其实面试官你本身就是在用面向对象的方式思考问题,
因为以面试官您的能力而言,如果来了一个软件项目的话,您从需求分析到设计到开发到测试,
虽然肯定都能完成,但是这样特别耗费时间,所以您为了提高效率,就需要去找一些具备具备专业编程经验的人来完成项目,
我正好就是那个具备专业编程经验的对象,您只要指挥我这个对象做事情就可以了,我会给您一个非常满意的结果
至于过程您不用管。所以面试官您就是在用面向对象的方式思考问题,来提高公司的效率。

其他示例:
1.买电脑和请人一起买电脑
2.自己开发和请人开发

面向对象的三大特征:继承、封装、多态。
在以后的开发过程中,其实就是在找对象用,没有对象,就创建一个对象。
找对象,建立对象,使用对象,并维护对象的关系。


二.类和对象的关系

类:就是对现实生活中事物的描述
对象:就是这类事物,实实在在存在的个体

示例:
需求,描述小汽车
描述事物其实就是在描述事物的属性和行为

分析:1.属性  轮胎数、颜色; 2.行为  运行

定义类其实就是在定义类中的成员
成员:成员变量(属性)、成员函数(行为、方法)

所以定义类,就是在描述事物,就是在定义属性和方法,属性和行为共同成为类中的成员。
这个类中不必有主函数,因为不用独立运行。

描述汽车的类:
class Car {//描述汽车的颜色String color = "red";//描述汽车的轮胎数int num = 4;//描述汽车的运行void run(){System.out.println("color="+color+"..."+"num="+num);}}


生成汽车的类:
public class CarTest {public static void main(String[] args) {//生成汽车,在Java中通过new操作符来完成//其实就是在堆内存中产生一个实体Car car = new Car();//car就是一个类类型的变量,类类型的变量指向对象//需求,将已有车的颜色改成蓝色,指挥该对象来完成,//指挥方式:对象名.属性名car.color = "blue";car.run();new Car().color = "green";//这是一个匿名对象,不过调用匿名对象的属性没有意义new Car().run();//匿名对象调用方法,只能调用一次。method(new Car());//匿名对象可以作为实际参数进行传递}//需求:汽修厂对汽车进行改装,来了的车都改为黑色、三个轮胎。public static void method(Car car){//将汽车的颜色改为黑色car.color = "black";//将汽车的轮胎数改为3个car.num = 3;car.run();}}

匿名对象
定义:没有名字的对象
匿名对象是对象的简化形式。

匿名对象的两种使用情况:
1.当对对象的方法仅使用一次调用时,可以使用匿名对象
2.匿名对象可以作为实际参数进行传递

注意:调用匿名对象的属性是没有意义的,因为调用完就找不到匿名对象了。

成员变量和局部变量的区别:
1.成员变量:定义在类中,作用于整个类
   局部变量:定义在函数、语句、局部代码块中,只在所属大括号区域有效。
2.成员变量:存在于堆内存的对象中
   局部变量:存在于栈内存的方法中
3.成员变量:随着对象的创建而存在,随着对象的消失而消失
   局部变量:随着所属区域的执行而存在,随着所属区域的结束而释放
4.成员变量:有默认初始化值,可以直接参与运算
   局部变量:没有默认初始化值,不能直接参与运算


三.封装

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

封装的好处:
1.将变化隔离
2.便于使用
3.提高重用性
4.提高安全型

封装的原则:
1.将不需要对外提供的内容都隐藏起来
2.把属性都隐藏起来,提供公共方法对其进行访问



四.private关键字

定义:私有,为权限修饰符,用于修饰类中的成员(变量、方法),
被修饰的内容在其他类中是不可以被访问的,所以私有只在本类中有效。

注意:私有仅仅是封装的一种表现形式
之所以对外提供访问方式,是因为在访问方式中加入逻辑判断等语句,对访问的数据进行操作,提高代码的健壮性。


五.构造函数

定义:构建创造对象时调用的函数

特点:1.函数名与类名相同,2.不用定义返回值类型,3.不可以写return语句

作用:给对象进行初始化

创建对象都必须要通过构造函数初始化。
一个类中如果没有定义过构造函数,那么系统会默认给该类添加一个空参数的构造函数。
如果在类中有了指定参数的构造函数,那么类中的默认构造函数就没有了。

注意:
1.默认构造函数的特点
2.多个构造函数是以重载的形式存在的

一般函数和构造函数有什么区别呢?
构造函数:对象创建时,就会调用与之对应的构造函数,对对象进行默认初始化。
对象创建时,只调用一次。
一般函数:对象创建后,需要函数功能时才调用。
对象创建后,可以被调用多次。

什么时候定义构造函数呢?
在描述事物时,该事物一存在就具备一些内容,这些内容都定义在构造函数中。
构造函数可以有多个,用于对不同的对象进行针对性的初始化。


六.构造代码块

作用:给所有对象进行初始化,对象一建立就运行,且优先于构造函数执行

和构造函数的区别:
1.构造代码块是给所有对象进行统一初始化的。
2.构造函数是给对应的对象进行初始化的。

构造代码块中定义的是不同对象共性的初始化内容,且和构造函数一样,在给对象进行初始化时执行一次。


七.this关键字

特点:this代表其所在函数所属对象的引用,换言之,this代表本类对象的引用。
简单的说,哪个对象在调用this所在的函数,this就代表哪个对象。

作用:
1.用于区分局部变量和成员变量同名的情况
2.用于构造函数间互相调用,不能用于一般函数间相互调用

应用:
当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象
但凡本类功能内部使用到了本类对象,都用this表示

注意:
1.构造函数间调用只能用this
2.this只能定义在构造函数的第一行,因为初始化动作要先执行。
person(String name){this.name = name;}person(String name,int age){this(name);this.age = age;}


八.static关键字

作用:用于修饰成员(成员变量和成员函数),它是一个修饰符

被修饰后的成员具备以下特点:
1.随着类的加载而加载,也随着类的消失而消失,生命周期最长
2.优先于对象存在,静态先存在,对象后存在
3.被所有对象所共享,在内存中单独存在
4.可以直接被类名调用,类名.静态成员

使用注意:
1.静态方法只能访问静态成员(方法、变量)
2.静态方法中不可以写this、super关键字
3.主函数是静态的

成员变量和静态变量的区别:
1.存放位置,静态变量随着类的加载而存在于方法区中,成员变量随着对象的建立而存在于堆内存中
2.生命周期,静态变量生命周期最长,随着类的消失而消失;成员变量的生命周期随着对象的消失而消失
3.调用方式,静态变量可以被对象调用,也可以被类名调用;成员变量只能被对象调用

什么时候使用静态变量呢?
当对象中出现了共享数据时,该数据可以被静态修饰
对象中的特有数据要定义成非静态存在堆内存中

什么时候定义静态函数呢?
当功能内部没有访问到非静态数据,或者对象的特有数据,那么该功能可以定义成静态的。

静态的利与弊
利:对对象的共享数据进行单独空间的存储,节省空间,可以直接被类名调用。
弊:生命周期过长,访问出现局限性(静态虽好,只能访问静态)


九.main函数

public static void main(String[ ] args)

主函数:这是一个特殊的函数,作为程序的入口。可以被JVM所识别和调用,且格式固定(除了args可以改,其它不行)
public:代表该函数的访问权限最大
static:代表主函数随着类的加载就已经存在了
void:代表主函数没有具体的返回值
main:不是一个关键字,但是这是一个特殊的单词,可以被JVM识别的固定名称
(Sring[ ] args):主函数的参数列表,参数类型是一个数组,该数组中的元素是字符串

静态的应用:工具类,方便在任意类中复用。
public class ArrayTool {public static void main(String[] args){int[] arr = {1,3,5,7,9};ArrayTool.bianLi(arr);}
<span style="white-space:pre"></span>//这是一个遍历数组的静态方法,可以直接被类名调用public static void bianLi(int[] arr){for(int x=0;x<arr.length;x++){if(x!=arr.length-1)System.out.print(arr[x]+",");elseSystem.out.print(arr[x]);}}}


十.帮助文档的制作

dos命令:javadoc  -d  myclass  -author  -version  ArrayTool.java

javadoc提取的类必须是public,否则报错。
/**这是一个可以对数组进行操作的工具类,该类提供了获取最值、排序等功能。@author 詹彦涛@version V1.0*/public class ArrayTool{/**空参数构造函数。*/private ArrayTool(){}//获取最大值/**获取一个整形数组中的最大值。@param arr 接收一个int类型的数组*/public static void getMax(int[] arr){int max = arr[0];for(int x=0;x<arr.length-1;x++){if(arr[x]>max){max = arr[x];}}System.out.println("max="+max);}//获取最小值/**获取一个整形数组中的最小值。@param arr 接收一个int类型的数组*/public static void getMin(int[] arr){int min = arr[0];for(int x=0;x<arr.length-1;x++){if(arr[x]<min){min = arr[x];}}System.out.println("min="+min);}//位置交换功能抽取(私有化)/**给int数组进行位置置换。@param arr 接收一个int类型的数组@param a 要置换的位置@param b 要置换的位置*/private static void swap(int[] arr,int a,int b){if(arr[a]>arr[b]){int temp = arr[a];arr[a] = arr[b];arr[b] = temp;}}//遍历/**用于打印数组中的元素,打印形式是:[elment1,element2...]@param arr 接收一个int类型的数组*/public static void printArr(int[] arr){System.out.print("[");for(int x=0;x<arr.length;x++){if(x!=arr.length-1){System.out.print(arr[x]+",");}else{System.out.println(arr[x]+"]");}}}}


十一.静态代码块

格式:
static{      静态代码块中的执行语句;}
特点,随着类的加载而执行,且只执行一次,并优先于主函数,用于给类进行初始化。


十二.对象的初始化过程

public class Person {<span style="white-space:pre"></span>private String name;<span style="white-space:pre"></span>private int age;<span style="white-space:pre"></span>private static String country = "CN";<span style="white-space:pre"></span><span style="white-space:pre"></span>Person(String name,int age){<span style="white-space:pre"></span>this.name = name;<span style="white-space:pre"></span>this.age = age;<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>public void setAge(int age){<span style="white-space:pre"></span>this.age = age;<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>public int getAge(){<span style="white-space:pre"></span>return age;<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>public void setName(String name){<span style="white-space:pre"></span>this.name = name;<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>public String getName(){<span style="white-space:pre"></span>return name;<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>public void speak(){<span style="white-space:pre"></span>System.out.println(this.name+"..."+this.age);<span style="white-space:pre"></span>}<span style="white-space:pre"></span>public static void showCountry(){<span style="white-space:pre"></span>System.out.println("country="+country);<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>public static void main(String[] args){<span style="white-space:pre"></span>Person p = new Person("zhangsan",25);<span style="white-space:pre"></span>}}

Person p = new Person("zhangsan",25);这句话都做了什么?
1.因为new用到了Person.class文件,所以会先找到Person.class文件并加载到内存中。
2.执行该类中的static代码块,如果有的话,给Person类进行初始化
3.在堆内存中开辟空间,分配内存地址
4.在堆内存中建立对象的特有属性,并进行默认初始化(name=null age=0 country=null)
5.对属性进行显示初始化(也就是属性初始化,如果没有赋值那么就是默认的值)
6.对对象进行构造代码块初始化
7.对对象进行对应的构造函数初始化
8.将内存地址赋给栈内存中的p变量


十三.单例设计模式

设计模式:解决某一类问题最行之有效的方法,Java中有23种设计模式。
单例设计模式:解决如何让一个类在内存中只存在一个对象的问题。
应用:比如程序的配置信息等

想要保证对象唯一性
1.为了避免其它程序过多的建立该类对象,先禁止其他程序建立该类对象
2.为了让其它程序可以访问到该类对象,可以先在本类中,自定义一个对象
3.为了方便其它程序对自定义对象的访问,可以对外提供一些访问方式

这三步用代码怎么体现呢?
1.将构造函数私有化
2.在类中创建一个本类对象
3.提供一个方法可以获取到本类对象

使用方法:
对于事物该怎么描述,还怎么描述
当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可

单例设计模式又有两种方式来实现,一种是懒汉式,一种是饿汉式。
懒汉式代码:
public class SingleDemo {private int num;public void setNum(int num){this.num = num;}public int getNum(){return num;}private SingleDemo(){}private static SingleDemo sd = new SingleDemo();public static SingleDemo getInstance(){return sd;}public static void main(String[] args) {SingleDemo s1 = SingleDemo.getInstance();s1.setNum(15);SingleDemo s2 = SingleDemo.getInstance();System.out.println(s2.getNum());}}
开发一般使用饿汉式,因为安全、简单
在上面的示例代码,Single类一进内存,就已经创建好了对象。

懒汉式代码:
public class SingleDemo2 {private SingleDemo2(){}private static SingleDemo2 sd = null;public static SingleDemo2 getInstance(){if(sd==null){sd = new SingleDemo2();}return sd;}}
懒汉式,对象是方法被调用时才初始化,也叫做对象的延时加载
Single类进内存,对象还没有存在,只有调用了getInstance 方法时,才建立对象。
但是上述代码不安全,有可能会建立多个对象,因此需要加入同步锁,来保证安全,因此效率低。代码如下
public class SingleDemo2 {private SingleDemo2(){}private static SingleDemo2 sd = null;public static SingleDemo2 getInstance(){if(sd==null){synchronized(SingleDemo2.class){if(sd==null){sd = new SingleDemo2();}}}return sd;}}


十四.继承

继承的作用:
1.提高了代码的复用性
2.让类与类之间产生了关系,有了这个关系,才有了多态的特性

注意:
千万不要为了获取其它类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系is a

Java语言中,Java只支持单继承,不支持多继承。
因为多继承容易来来隐患,当多个父类中定义了相同名字的功能,虽然功能内容可能不同,但是子类对象不确定要运行哪一个。
虽然不支持多继承,但是Java用另外一种体现形式来完成这种表示,那就是多实现。

Java支持多层继承,也就是一个继承体系。
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系中父类的描述,因为父类中定义的是该体系中共性功能,
通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。

在具体调用时,要创建子类的对象,为什么呢?
1.因为有可能父类不能创建对象
2.创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
class Person{String name;int age;}class Student extends Person{void study(){System.out.println("good study");}}class Worker extends Person{void work(){System.out.println("good work");}}

子父类中成员的特点
类中成员有:1.变量,2.函数,3.构造函数

变量的特点:
如果子类中出现非私有的同名成员变量时,子类要访问本类中的同名变量,用this;
子类如果要访问父类中的同名变量,用super。

super的使用和this几乎一致,this代表的是本类对象的引用,super代表的是父类对象的引用。
class Fu{int num = 4;}class Zi extends Fu{int num = 5;void show(){System.out.println(num);}}class ExtendsDemo2 {public static void main(String[] args) {Zi z = new Zi();z.show();//System.out.println(z.num+"...."+z.num);}}

子父类中函数的特点

当子类出现和父类一模一样的函数,当子类对象调用该函数,会运行子类函数的内容,
如同父类函数被覆盖一样,这种情况是函数的另一个特性,重写(覆盖)。

当子类继承父类,沿袭了父类的功能,在子类中,虽然子类具备了该功能,但是功能的内容却和父类不一致,
这时,没有必要重新定义新功能,而是使用覆盖,保留父类的功能定义,并重写功能内容。

覆盖的注意事项:
1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2.静态只能覆盖静态。

记住:
重载:只看同名函数的参数列表
重写:子父类方法要一模一样
class Fu{void show(){System.out.println("Fu show");}void speak(){System.out.println("vb");}}class Zi{void show(){System.out.println("Zi show");}public void speak(){System.out.println("java");}}class ExtendsDemo3 {public static void main(String[] args) {Zi z = new Zi();z.speak();}}

子父类中的构造函数

在对子类对象进行初始化时,父类的构造函数也会运行。因为子类的构造函数默认第一行有一条隐式的语句super()。
super():这条语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。

为什么子类一定要访问父类中的构造函数呢?
因为父类中数据子类可以直接获取,所以子类对象在建立时,需要查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。

注意:super语句一定要放在子类构造函数的第一行。

结论:
1.子类所有的构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式super()。
2.当父类中没有空参数的构造函数时,子类必须手动通过super语句来指定要访问的构造函数。
3.当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。
子类中至少会有一个构造函数会访问父类中的构造函数。

class Fu{int num;Fu(){num = 60;System.out.println("Fu run");}Fu(int x){System.out.println("Fu...."+x);}}class Zi extends Fu{int num = 50;Zi(){//super();//super(3);System.out.println("Zi run");}Zi(int x){this();//super();//super(3);System.out.println("Zi...."+x);}}public class Test {public static void main(String[] args) {Zi z = new Zi(4);System.out.println();}}


十五.final关键字

定义:最终。是一个修饰符。
特点概述:
1.可以修饰类、函数、变量
2.被final修饰的类不可以被继承。为了避免被继承,被子类复写。
3.被final修饰的方法不可以被覆写。
4.被final修饰的变量是一个常量且只能赋值一次,既可以修饰成员变量,也可以修饰局部变量。
  当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便于阅读。
  而这些值不需要改变,所以加上final修饰,作为常量,常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接。
5.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。

class Demo{final int X = 3;public static final double MY_PAI = 3.14;final void show1(){}}class SubDemo extends Demo{//因为show1被final修饰,所以无法被复写}

十六.抽象类

为什么会出现抽象类?
当多个类中出现相同功能,但是功能主体不同的情况时,这时可以进行向上抽取,但是只抽取功能定义,而不抽取功能主体。

抽象:即看不懂
抽象类的特点:
1.抽象方法一定在抽象类中。
2.抽象方法和抽象类都必须被abstract关键字修饰。
3.抽象类不可以用new创建对象,因为调用抽象方法没有意义。
4.抽象类中的抽象方法如果要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用。
   如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

抽象类和一般类没有太大的不同
该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。
这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体。通过抽象方法来表示。

抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法,抽象类不可以被实例化。
当然,抽象类中也可以没有抽象函数,只有一般函数,但是这种情况比较少见。

特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。

abstract class Student{abstract void study();void sleep(){System.out.println("躺着");}}class BaseStudent extends Student{void study(){System.out.println("base study");}}class AdvStudent extends Student{void study(){System.out.println("adv study");}}class Abstract{public static void main(String[] args) {new BaseStudent().study();}}

抽象类练习:
/*假如我们在开发一个系统时,需要对员工进行建模,员工包含3个属性;姓名、工号、工资。经理也是员工,除了含有员工的属性外,另外还有一个奖金属性。请使用继承的思想设计出员工类和经理类。要求类中必须提供必要的方法进行属性访问。*/
//员工类和经理类的抽取的共性类abstract class Employee{private String name;private String id;private double pay;Employee(String name,String id,double pay){this.name = name;this.id = id;this.pay = pay;}public void setName(String name){this.name = name;}public String getName(){return name;}public String getId() {return id;}public void setId(String id) {this.id = id;}public double getPay() {return pay;}public void setPay(double pay) {this.pay = pay;}public abstract void work();}
//员工类class Pro extends Employee{Pro(String name,String id,double pay){super(name,id,pay);}public void work(){System.out.println("pro work");}}
//经理类class Manager extends Employee{private double bonus;Manager(String name,String id,double pay,double bonus){super(name,id,pay);this.bonus = bonus;}public double getBonus() {return bonus;}public void setBonus(double bonus) {this.bonus = bonus;}public void work(){System.out.println("manager work");}}public class AbstractTest {public static void main(String[] args) {Pro p = new Pro("zhangsan","001",100.88);p.setPay(888.88);System.out.println(p.getPay());}}


十七.模板方法设计模式

1.需求:获取一段程序运行的时间。
2.原理:获取程序开始和结束的时间并相减
3.获取时间:System.currentTimeMillis( );
4.这种方式,叫模板方法设计模式
5.模板方法设计模式:在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,
而确定的部分在使用不确定的部分,那么这时就将不确定的那部分暴露出去,由该类的子类去完成。
abstract class GetTime{public void getTime(){long start = System.currentTimeMillis();runCode();long end = System.currentTimeMillis();System.out.println("毫秒:"+(end-start));}public abstract void runCode();}class SubTime extends GetTime{public void runCode(){for(int x=0;x<500;x++){System.out.println(x);}}}public class TemplateDemo {public static void main(String[] args) {SubTime sb = new SubTime();sb.getTime();}}


十八.接口

接口的特点:
1.接口是对外暴露的规则
2.接口是程序的功能扩展
3.接口可以用来多实现。
4.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
5.接口与接口之间可以有继承关系。

初期理解,可以认为是一个特殊的抽象类,但是这个抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。

class用于定义类,interface用于定义接口。

接口定义时,格式特点:
1.接口中常见定义:常量,抽象方法。
2.接口中的成员都有固定修饰符
常量:public static final
方法:public abstract

记住:
1.接口中的成员都是public的。
2.接口是不可以创建对象的,因为有抽象方法,需要被子类实现,
子类对接口中的抽象方法全部覆盖后,才可以实例化,否则子类是一个抽象类。
3.接口可以被类多实现,也是对多继承不支持的转换形式,Java支持多实现。
interface Inter{public static final int NUM = 3;public abstract void show();}interface InterA{public abstract void show();}class Demo{public void function(){}}class Test extends Demo implements Inter,InterA{//虽然Inter和InterA里都有show方法,但是都是抽象方法,没有方法体,所以可以一次全覆盖。public void show(){}}class InterfaceDemo {public static void main(String[] args) {Test t = new Test();System.out.println(t.NUM);System.out.println(Test.NUM);System.out.println(Inter.NUM);}}


十九.多态

多态:可以理解为事物存在的多种体现形态。

人:男人、女人
动物:猫、狗

猫 x  =  new 猫();    动物  x = new 猫();

多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接受自己的子类对象。

多态的前提
必须是类与类之间有关系,要么继承,要么实现。
同时还有一个前提:存在覆盖。

多态的好处
多态的出现大大的提高了程序的扩展性

多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
<span style="font-size:14px;">abstract class Animal{abstract void eat();}class Cat extends Animal{public void eat(){System.out.println("吃鱼");}public void catchMouse(){System.out.println("抓老鼠");}}class Dog extends Animal{public void eat(){System.out.println("吃骨头");}public void kanJia(){System.out.println("看家");}}class DuoTaiDemo {public static void main(String[] args) {<span style="white-space:pre"></span>Animal a = new Cat();function(a);}public static void function(Animal a){a.eat();}}</span>
Animal  a = new Cat( );//这句话中的a类型得到了提升,向上转型。

但是这时如果想要调用猫特有方法时,如何操作呢?
这个时候要将父类的引用,转成子类类型,向下转型。
Cat  c = (Cat) a;

但是千万不能出现这样的操作,就是将父类对象转成子类类型。
我们能转换的是父类的引用指向自己的子类对象时,该引用可以被提升,也可以被强制转换。

记住:多态自始至终都是子类在做着变化!

在多态中,(非静态)成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
动态绑定在调用函数的对象上。

在多态中,成员变量(静态成员函数)的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
静态绑定在静态所述的类中。

示例一:
class Fu{int num = 5;void Method1(){System.out.println("fu_method1");}void Method2(){System.out.println("fu_method2");}static void Method4(){System.out.println("fu_method4");}}class Zi extends Fu{int num = 8;void Method1(){System.out.println("zi_method1");}void Method3(){System.out.println("zi_method3");}static void Method4(){System.out.println("zi_method4");}}class DuoTaiDemo2{public static void main(String[] args) {/*非静态成员函数:编译时看左边,运行时看右边。Fu f = new Zi();f.Method1();f.Method2();//f.Method3();*//*成员变量:编译运行都看左边。Fu f = new Zi();System.out.println(f.num);Zi z = new Zi();System.out.println(z.num);*//*静态成员函数:编译运行都看左边。Fu f = new Zi();f.Method4();Zi z = new Zi();z.Method4();*/}}

实例二:
/*需求:电脑运行示例,电脑运行基于主板,扩展基于PCI插槽。*/interface PCI{public abstract void open();public abstract void close();}class MainBoard{public void run(){System.out.println("MainBoard run");}public void usePCI(PCI p){if(p!=null){p.open();p.close();}}}class NetCard implements PCI{public void open(){System.out.println("NetCard open");}public void close(){System.out.println("NetCard close");}}class SongCard implements PCI{public void open(){System.out.println("SongCard open");}public void close(){System.out.println("NetCard close");}}class DuoTaiDemo5 {public static void main(String[] args) {MainBoard mb = new MainBoard();mb.run();mb.usePCI(new NetCard());mb.usePCI(new SongCard());}}


二十.内部类

内部类的访问规则:
1.内部类可以直接访问外部类中的成员,包括私有。
   之所以可以直接访问,是因为内部类中持有了一个外部类的引用,格式:外部类名.this
2.外部类要访问内部类,必须建立内部类对象。

内部类的访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
格式:外部类名.内部类名  变量名 =  外部类对象.内部类对象。
    Outer.Inner  in = new Outer().new Inner();
2.当内部类定义在外部类的成员位置上,就可以被成员修饰符所修饰
比如:private:将内部类在外部类中进行封装。
      static:内部类就是具备static的特性。
    当内部类被static修饰后,只能直接访问外部类中的static,出现了访问局限。

    在外部其他类中,如何直接访问static修饰的内部类的非静态成员呢?
      new Outer.Inner().function;

    在外部其他类中,如何直接访问static内部类的静态成员呢?
    Outer.Inner.function();

注意:当内部类中定义了静态成员,该内部类必须是static的。
    当外部类中的静态方法访问内部类时,内部类也必须是静态的。

内部类的由来:
当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事务在使用外部事物的内容。
<span style="font-size:14px;">class Outer{private static int x = 3;static class Inner{//静态内部类static void function(){System.out.println("Inner:"+x);}}static class Inner2{void show(){System.out.println("inner2 show");}}public static void method(){//Inner.function();new Inner2().show();}}class InnerClassDemo2{public static void main(String[] args) {Outer out = new Outer();out.method();/*直接访问内部类中的成员Outer.Inner in = new Outer().new Inner();in.function();*/}}</span>

内部类定义在局部时:
1.不可以被成员修饰符修饰
2.可以直接访问外部类中的成员,因为还持有外部类中的引用。
   但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。
class Outer{int x = 3;void method(final int a){final int y = 4;class Inner{void function(){System.out.println(a);}}new Inner().function();}}class InnerClassDemo3 {public static void main(String[] args) {new Outer().method(5);}}

匿名内部类:
1.匿名内部类其实就是内部类的简写格式
2.定义匿名内部类的前提:内部类必须是继承一个类或者实现接口
3.匿名内部类的格式:new 父类或者接口(){ 定义子类内容 }
4.其实匿名内部类就是一个匿名子类对象
5.匿名内部类中定义的方法最好不要超过两个。

abstract class AbsDemo{abstract void show();}class Outer{int x =3;/*内部类class Inner extends AbsDemo{void show(){System.out.println("show:"+x);}void abc(){System.out.println("haha");}}*/public void function(){//new Inner().show();//AbsDemo a = new Inner;/*Inner in = new Inner();in.show();in.abc();*/AbsDemo d = new AbsDemo(){void show(){System.out.println("x="+x);}void abc(){System.out.println("haha");}};d.show();//d.abc();编译失败,因为建立了多态,父类中没有abc()方法。}}class InnerClassDemo4 {public static void main(String[] args) {new Outer().function();}}


二十一.异常

定义:就是程序在运行时出现不正常的情况。
异常的由来:问题也是现实生活中一个具体的事物,也可以通过Java类的形式进行描述,并封装成对象。
其实就是Java对不正常情况进行描述后的对象体现。

对于问题的划分有两种,一种是严重的问题,一种是非严重的问题。

对于严重的,Java通过Error类进行描述。对于Error一般不编写针对性的代码对其进行处理。
对于非严重的,Java通过Exception类进行描述,对于Exception可以使用针对性的处理方式进行处理。

异常体系:
Throwable
|--Error
|--Exception
|--RuntimeException

异常的处理

Java提供特有的语句进行处理。
try{需要被监测的代码。}catch(异常类 变量){处理异常代码;(处理方式)}finally{一定会执行的语句。}

对捕获到的异常对象进行常见方法的操作
1.getMessage():获取异常信息
2.printStackTrace():获取异常名称,异常信息,异常出现的位置。

class Demo{int div(int a,int b){return a/b;}}class ExceptionDemo {public static void main(String[] args) {Demo d = new Demo();try{int x = d.div(4,1);System.out.println("x="+x);}catch(Exception e){//Exception e = new ArithmeticException();System.out.println("除零啦!");System.out.println(e.getMessage());//异常信息System.out.println(e.toString());//异常名称:异常信息e.printStackTrace();//异常名称,异常信息,异常出现的位置。/*JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。*/}System.out.println("Over");}}

在函数上声明异常
便于提高安全性,让调用者进行处理,不处理则编译失败。

对多异常的处理:
1.声明异常时,建议声明更为具体的异常,这样处理的可以更具体。
2.对方声明几个异常,就应该有几个catch块,不要定义多余的catch块,
如果多个catch块中的异常出现继承关系,父类异常catch块放在最后。

建议在进行catch处理时,catch中一定要定义具体处理方式。
不要简单定义一句:e.printStackTrace(),也不要简单的书写一条打印语句。

class Demo{//在功能上通过throws关键字声明了该功能可能会出现的问题。int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException{int[] arr = new int[a];System.out.println(arr[4]);return a/b;}}class ExceptionDemo2 {public static void main(String[] args) {Demo d = new Demo();try{int x = d.div(4,1);System.out.println("x="+x);}catch(ArithmeticException e){System.out.println(e.toString());System.out.println("被零除了!");}catch(ArrayIndexOutOfBoundsException e){System.out.println(e.toString());System.out.println("角标越界了!");}catch(Exception e){System.out.println(e.toString());}System.out.println("Over");}}

为什么要自定义异常?

因为项目中会出现特有问题,而这些问题并未被Java所描述并封装对象,
所以对于这些特有的问题可以运行Java对问题进行封装的思想,将特有问题,进行自定义的异常封装。

当在函数内部出现throw抛出异常对象,那么就必须要给出对应的处理动作。
要么在函数内部try catch处理,要么在函数上声明让调用者处理。
一般情况下,函数内出现异常,函数上需要声明。

如何自定义异常信息呢?
因为父类中已经把异常信息的操作都已经完成了。
所以子类只要在构造时,将异常信息传递给父类,通过super语句。
那么就可以直接通过getMessage方法获取自定义的异常信息了。

自定义异常:必须是自定义类继承Exception。
继承Exception原因:
异常体系有一个特点,因为异常类和异常对象都可以被抛出。
他们具备可抛性,这个可抛性是throwable这个体系所独有特点。
只有这个体系中的类和对象,才可以被throw和throws操作。

throw和throws的区别?
1.throw使用在函数内,throws使用在函数上。
2.throws后面跟的是异常类,并且可以跟多个,类与类之间用逗号隔开,throw后面跟的是异常对象。

class FuShuException extends Exception{<span style="white-space:pre"></span>FuShuException(String msg){<span style="white-space:pre"></span>super(msg);<span style="white-space:pre"></span>}}class Demo{<span style="white-space:pre"></span>int div(int a,int b)throws FuShuException{<span style="white-space:pre"></span>if(b<0){<span style="white-space:pre"></span>throw new FuShuException("除数为负数了!");<span style="white-space:pre"></span>}<span style="white-space:pre"></span>return a/b;<span style="white-space:pre"></span>}}class ExceptionDemo {<span style="white-space:pre"></span>public static void main(String[] args) <span style="white-space:pre"></span>{<span style="white-space:pre"></span>Demo d = new Demo();<span style="white-space:pre"></span>try{<span style="white-space:pre"></span>d.div(5,-1);<span style="white-space:pre"></span>}catch(FuShuException e){<span style="white-space:pre"></span>e.printStackTrace();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>System.out.println("over");<span style="white-space:pre"></span>}}

Exception中有一个特殊的子类异常:RuntimeException(运行时异常)。
1.如果在函数内抛出了该异常,函数上可以不用声明,编译一样通过。
2.如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。

之所以不用在函数上声明,是因为不需要调用者处理。
当该异常发生后,希望程序停止,是因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

自定义异常时,如果由于该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。

对于异常分为两种:
1.编译时被检测的异常。
2.编译时不被检测的异常(运行时异常,RuntimeException及其子类)

class FuShuException1 extends RuntimeException{FuShuException1(String msg){super(msg);}}class Demo1{int div(int a,int b){if(b==0){throw new ArithmeticException();}if(b<0){throw new FuShuException1("除数为负数了!");}return a/b;}}public class Exception2 {public static void main(String[] args) {Demo1 d = new Demo1();d.div(4,-1);System.out.println("over");}}

示例:毕老师用电脑讲课。  名词抽取法:老师、讲课

开始思考上课中可能出现的异常、
比如:电脑蓝屏
    电脑冒烟

要对问题进行描述,封装成对象。

电脑蓝屏,可以重启后继续讲课。
但是电脑冒烟后,出现课时无法继续进行的情况,且课时计划无法完成。
class LanPingException extends Exception{LanPingException(String msg){super(msg);}}class MaoYanException extends Exception{MaoYanException(String msg){super(msg);}}class NoPlanException extends Exception{NoPlanException(String msg){super(msg);}}class Computer{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 comp;public String getName(){return name;}Teacher(String name){this.name = name;comp = new Computer();}public void teach()throws NoPlanException{try{comp.run();}catch(LanPingException e){System.out.println(e.toString());comp.reset();}catch(MaoYanException e){System.out.println(e.toString());test();throw new NoPlanException("课时无法继续进行!");}System.out.println("开始讲课!");}public void test(){System.out.println("做练习!");}}public class ExceptionTest {public static void main(String[] args) {Teacher t = new Teacher("毕老师");try {t.teach();} catch (NoPlanException e) {System.out.println("换老师或者放假!");}}}

finally代码块:定义一定执行的代码,通常用于关闭资源。

try catch的几种格式:
第一种
try{}catch(){}
第二种
try{}catch(){}finally{}
第三种
try{}finally{}
记住一点:catch是用于处理异常的,
如果没有catch就代表异常没有被处理过,如果该异常是编译时异常,那么必须声明。

异常在子父类覆盖中的体现:
1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2.如果父类方法抛出了多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
3.如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
   如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。


包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。

不同包中的子类还可以直接访问父类中被protected权限修饰的成员。

包与包之间可以使用的权限只有两种:public  protected。

public protected defaultprivate
同一类中  ok            ok             ok          ok
同一包中  ok            ok             ok          
不同包中(子类)  ok            ok  
不同包中  ok




0 0