黑马程序员---Java基础---面向对象:多态、内部类、异常

时间:2022-10-01 00:48:25
------- android培训java培训、期待与您交流! ----------
面向对象:多态、内部类、异常 一、多态 1.含义 多态即某一事物存在的多种形态,在java中我们指父类的引用指向或接受自己的子类对象。 例如:如果Animal类是Dog的父类,Animal animal = new Dog()
2.多态的好处
多态的出现大大的提高了代码的扩展性和后期可维护性。
3.多态的前提
(1)类与类之间要有关系,要么继承,要么实现。
(2)一般还要有:存在覆盖。
4.多态的弊端
只能使用父类的引用的访问父类中的成员。
5.多态的转型
转型包括向上转型和向下转型
向上转型:Animal animal=new Dog();为向上转型。即父类引用指向子类对象
向下转型:Dog c=(Dog)animal;为向下转型。将父类引用转为子类引用,向下转型主要用于调用子类的特有方法。
6.多态的特点 (1)非静态成员变量和静态成员 在编译和运行时,都参照引用变量所在的类中的非静态成员变量和静态成员 例子:
package test;
class Animal
{
static int value = 5;
int num = 5;

public void method()
{
System.out.println(num);
}
static void showvalue()
{
System.out.println(value);
}
static void show()
{
System.out.println("Animal static show");
}
}
class Dog extends Animal
{
//子类中也定义了和父类一样的变量和静态成员
static int value = 6;
int num = 6;

static void showvalue()
{
System.out.println(value);
}
static void show()
{
System.out.println("Dog static show");
}
}

public class Test7
{
/**
* @param args
*/
public static void main(String[] args)
{
Animal animal = new Dog();

animal.method(); //结果为:5
animal.showvalue(); //结果为:5
animal.show(); //结果为:Animal static show
}
}
(2)非静态成员函数
1> 在编译时,参照引用变量所在的类中的成员函数,如果类中没有该函数,则会报错。
2> 在运行时,实际执行的是对象所在类中的成员函数。 例子:
package test;
class Animal
{
int num = 5;
void show()
{
System.out.println("Animal static show--" + num);
}
}
class Dog extends Animal
{
int num = 6;
void show()
{
System.out.println("Dog static show--" + num);
}
void method()
{}
}

public class Test7
{
/**
* @param args
*/
public static void main(String[] args)
{
Animal animal = new Dog();

animal.show(); //结果为:Dog static show--6
//animal.method(); //Animal类中无此函数,编译时会报错
Dog dog = (Dog)animal; //向下转型后,可以调用子类特有的方法
dog.method();
}
}
注意 如果子类中的函数覆盖了父类中的函数,并且该函数中访问子父类中都有的变量,那么子类函数中访问的是子类变量。

二、内部类 1.定义 将一个类定义在另一类的内部,这个类就称为内部类(内置类、嵌套类) 2.访问特点 (1)内部类可以直接访问它所在的外部类的成员,包括私有成员。 (2)外部类要访问内部类的成员,必须要建立内部类对象。 (3)之所以内部类可以直接访问其所在的外部类中的成员,是因为内部类只有了外部类的的引用,格式为:外部类      名.this
3.内部类定义在外部类中的位置 (1)访问格式 当内部类Inner定义在外部类Outer的成员位置上,而且非私有,可以在外部其他类中之间建立内部类对象,格式为:Outer.Inner in = newOuter().new Inner(); 例子:
package test;

class Outer
{
private int num = 5;
class Inner
{
int num = 6;

//可以直接访问外部类的变量,如果内部类和外部类有同名变量,可以用下面方式区分
void show()
{
int num = 7;

System.out.println(Outer.this.num); //外部类的num
System.out.println(this.num); //内部类的num
System.out.println(num); //局部变量num
}

//内部类可以直接访问外部类的方法
void inMethod()
{
method();
}
}
void method()
{
//外部类使用内部类中成员,必须建立内部类对象
Inner in = new Inner();
in.show();
}
}

public class Test8
{

/**
* @param args
*/
public static void main(String[] args)
{
//建立内部类对象
Outer.Inner ouin = new Outer().new Inner();
ouin.show();
}

}

(2)当内部类在成员位置上,就可以被成员修饰符修饰  

比如private:将内部类在外部类中进行封装

       static:内部类就具备static的特性

(3)当内部类被static修饰后,只能直接访问外部类的静态成员,出现了局限性

在其他外部类中访问static内部类的非静态成员:

new Outer.Inner().function();

在其他外部类中访问static内部类的静态成员:

Outer.Inner.function

注意

1.当内部类中定义了静态成员,该内部类必须是静态内部类

2.当外部类中的静态方法访问内部类时,内部类也必须是静态的

例子:

package test;

class Outer
{
private static int num = 5;
static class Inner
{
static int num = 6;

//静态内部类只可以访问外部类的静态成员,如果内部类和外部类有同名变量,可以用下面方式区分
static void show()
{
int num = 7;

System.out.println(Outer.num); //外部类的num
System.out.println(Inner.num); //内部类的num
System.out.println(num); //局部变量num
}

//内部类可以直接访问外部类的方法
void inMethod()
{
System.out.println("hello");
}
}
}

public class Test8
{

/**
* @param args
*/
public static void main(String[] args)
{
//建立内部类对象
Outer.Inner ouin = new Outer.Inner();
ouin.inMethod(); //访问非静态成员
Outer.Inner.show(); //访问静态成员
}

}
4.内部类定义在外部类中的局部位置

(1)不可以被成员修饰符修饰

(2)可以直接访问外部类中的成员,因为还持有外部类中的引用;但是不可以访问它所在的局部中的变量,只能访

问被final修饰的局部变量。

例子:

package test;

class Outer
{
private int num = 5;
void method()
{
final int num = 6;
class Inner
{
void show()
{
System.out.println(Outer.this.num); //局部内部类可以直接访问外部类中的成员
System.out.println(num); //局部内部类只可访问被final修饰的局部变量
}
}
Inner in = new Inner();
in.show();
}

}

public class Test8
{

/**
* @param args
*/
public static void main(String[] args)
{
Outer out = new Outer();
out.method();
}
}
5.匿名内部类
(1)匿名内部类其实就是内部类的简写格式,
(2)定义匿名内部类的前提:内部类必须是继承一个类或者实现一个接口
(3)匿名内部类的格式:new 父类或者接口(){定义子类内容}
(4)其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖,即带内容的对象
(5)匿名内部类中定义的方法最好不要超过三个。

例子:

package test;

class Outer //此处可以是类也可以是接口
{
void method()
{
System.out.println("heihei");
}
}

class InterTest
{
//补足代码,通过匿名内部类
public static Outer function()
{
new Outer()
{
public void method()//可以复写父类方法
{
System.out.println("hiahia");
}
//可以定义自己的成员
int num = 6;
public void method1()
{
System.out.println("我自己的方法" + num);
}
}.method1();

//返回一个Outer类型的对象
return
new Outer()
{
public void method1()//可以自定义方法
{
System.out.println("我自己的方法");
}
};
}
}
public class Test6
{

/**
* @param args
*/
public static void main(String[] args)
{
//不能在此处调用method1()方法,因为此处是多态,父类Outer中没有定义method1()方法,
//method1()方法是Outer类的子类的特有方法
InterTest.function().method();
}
}


三、异常 1.异常 异常就是指程序在运行时出现了不正常的情况。

2.异常由来

问题也是现实生活中一个具体的事物,可以通过java的类的形式进行描述,并封装成对象。异常其实就是java对不正常情况进行描述后的对象体现。

3.异常体系

Throwable

    |-------Error:严重的异常通过Error类来描述,不编写针对代码对其处理

    |-------Exception:非严重的异常通过Exception类来描述

Error和Exception的子类名都是以父类名作后缀。

4.异常处理

try

{

    需要被检测的代码

}

catch(异常类 变量)

{

          处理异常的代码:处理方式

}

finally

{

   一定会执行的代码

}

finally代码块只有一种情况不会被执行,就是在之前执行了System.exit(0);

5.throws和throw

(1)throws用于标示函数暴露出的异常

(2)throw用于抛出异常对象

(3)throws和throw的区别

1> throws用在函数上,后跟异常类名

2> throw用在函数内,后跟异常类对象

注意

throw单独存在时,下面不要有其他语句,因为执行不到。

6.异常的处理方式

如果调用了有异常的函数,并且在函数上声明了异常,调用者有两种处理方式:要么通过try catch语句,对异常进行处理;要么用throws再抛出去

7.对多异常的处理

(1)声明异常时,建议声明更为具体的异常,这样处理的可以更具体

(2)对方声明几个异常,就对应有几个cathc块,不要定义多余的catch块如果多个catch块中的异常有几成关系,父

类异常catch块放在最下面

例子:

package com.itheima;

//异常的两种处理方式:要么try catch 要么用throws再抛出去
class Tool
{
//当在方法上声明异常后,调用者必须要处理,要么try,要么throws
public static int getQuotient(int a, int b) throws Exception
{
int x = a/b;
//当b=0,上一句代码出现异常,就结束了整个方法,此句代码不会执行。
System.out.println("x=" + x);
return x;
}
}
public class Test022
{

/**
* @param args
*/
public static void main(String[] args) throws Exception //处理方法1:可将异常向外抛
{

int x = 0;
try //处理方法2:捕捉异常并处理异常
{
x = Tool.getQuotient(4, 1);
} catch (Exception e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(x); //异常处理后程序可以继续向下执行
System.out.println("hiahia");
}
}


8.自定义异常

(1)自定义异常的由来

因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象,对于这些特有问题,我们可以按照java对

问题封装的思想,对特有问题进行自定义的异常封装。

(2)如何自定义异常

1> 创建一个类,然后继承Exception或其子类。

2> 通过构造函数自定义异常信息。

3> 通过throw将自定义异常对象抛出。

因为父类中已经把异常信息的操作都完成了,子类在创建时,将异常信息通过super语句传递给父类,就可以直接通

过getMessage()方法获取自定义的异常信息。

例子:

package com.itheima;

class myException extends Exception
{
/**
*
*/
private static final long serialVersionUID = 4548790691334996233L;

myException(String message)
{
super(message);
}
}
class Tool_1
{
public static int getQuotient(int a, int b) throws myException
{
if(b == 0)
throw new myException("除零了!"); //方法内部抛出异常对象,两种处理方式:抛出或在方法内捕捉
return a/b; //如果抛出的是RuntimeException及其子类对象则不需要在方法上声明
}
}
public class Test023
{

/**
* @param args
*/
public static void main(String[] args) throws myException
{
int x = Tool_1.getQuotient(4, 0);
System.out.println(x);
}
}

(3)继承Exception原因

异常体系有一个特点即异常类和异常对象都能被抛出,都具有可抛性,是Throwable这个体系的独有特点,只有这个

体系中的类和对象才可以被throw和throws操作。

9.RuntimeException

(1)RuntimeException及其子类的对象如果在方法中被throw抛出,可以不用在函数上声明。

(2)如果在方法上声明了RuntimeException及其子类异常,调用者可以不用处理。

由于在方法内部抛出RuntimeException异常不需要在方法上声明,所以调用者根本不知道被调用的方法会产生异常,

就不会写try或者throws,程序会直接停掉。

(3)异常分两种:

1> 编译时被检测的异常。

2> 编译时不被检测的异常(运行时异常,RuntimeException以及其子类)。

10.异常在子父类方法覆盖中的规则

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

(2)如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类方法异常的子集。

(3)如果父类或者接口的方法中没有异常抛出,子类在覆盖方法时也不可以抛出异常,只能在方法内trycatch。