------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
4.1 继承的概述
4.1.1 概念
在程序设计中,很多设计出来的类存在这样的包含关系,这样一个类的内部会包含和其他类类似的特征和属性,如果在设计时以另外一个类作为基础进行设计,这种特性就是面向对象的继承性。
说明:
Java中支持单继承,不直接支持多继承,但对C++中的多继承机制进行了改良。
子类可以直接访问父类中的非私有的属性和行为。
4.1.2 继承的好处
1. 提高了代码的复用性。
2. 让类与类之间产生了关系,给第三个特征多态提供了前提。
4.1.3 单继承和多重继承
1. 单继承:一个子类只能有一个直接父类。
2. 多继承:
一个子类可以有多个直接父类(Java中不允许,进行改良),不直接支持,是因为多个父类中有相同成员,会产生子类调用的不确定性。在Java中是通过“多实现”的方式来体现。
3. 多层继承(多重继承):C继承B,B继承A。会出现继承体系。
4.1.4 定义继承
1. 什么时候定义继承
当类与类之间存在着所属关系的时候,就定义继承。xxx是yyy中的一种。 xxx extends yyy
.
2. 继承的语法格式
访问控制符 [修饰符] class 类名 extends 父类名 {
……
}
4.2 继承的特点
1. Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error
2. Java支持多层继承(继承体系)。
class A{}
class B extends A{}
class C extends B{}
3. Java中多个子类可以同时继承一个父类
注意:当要使用一个继承体系时:
(1) 查看该体系中的顶层类,了解该体系的基本功能。(向上抽取)
(2) 创建体系中的最子类对象,完成功能的使用。
4.3 super关键字
4.3.1 子父类中成员变量的特点
1. 当本类的成员和局部变量同名用this区分。
2. 当子父类中的成员变量同名用super区分父类。
this和super的用法很相似。
this:代表一个本类对象的引用。
super:代表一个父类空间。
注意:子类不能直接访问父类私有内容,可以通过getXxx间接访问。
4.3.3.1 实例
class Fu { // 开发中基本没有
private int num = 4;
public int getNum() {
return num;
}
}
class Zi extends Fu {
private int num = 5;
void show() {
System.out.println(this.num+"....."+super.getNum());
}
}
输出: 5……4
4.3.3.2 实例内存图解
4.3.2 super关键字的注意事项
1. 在子类的继承中,子类只能继承父类的成员变量和成员方法,而不能继承
父类的构造方法。
如果子类想调用父类的构造方法,可以在自己的构造方法中使用super(【参数1,参数2……】)的方式来调用。
2. 使用super()调用父类构造方法时,它也必须放在构造方法的第一行。
3. 在子类的构造方法中如果没有显示的调用父类的构造方法(并且没有通过
this()调用本类其它的构造方法),构造方法的第一行会有一句隐式super语句默认调用父类的无参构造方法。
在定义一个类时,如果有了一个有参的构造方法,最好在定义一个无参的构造方法,否则运行容易出错。
4.4 函数覆盖(重写)
4.4.1 子父类中成员函数的特点-覆盖
覆盖的概念:当子父类中初相成员函数一模一样(函数名一样、返回值一样、参数列表一致)的情况,会运行子类的函数,这种现象,称为覆盖操作。覆盖也成为重写、覆盖、override。
(子类不用的情况下,父类可以用,当不想用父类的时候可以用子类的。)
区分函数两个特性:
1. 重载:同一个类中
2. 覆盖:子类中
4.4.1.1 实例
class Fu {
public static void show() {
System.out.println("fu show run");
}
}
class Zi extends Fu {
public static void show() {
System.out.println("Zi show run");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
4.4.1.2 实例内存图解
4.4.2 覆盖的注意事项
1. 子类方法覆盖父类方法时,子类权限必须要大于等于父类的权限。
如: 子-public 父-默认
2. 静态只能覆盖静态,或被静态覆盖。(少见)
3. 子类重写的方法抛出的异常不能超过父类相应方法抛出的异常
4.4.3 什么时候使用覆盖
当一个类进行子类的扩展时,子类需要保留父类的功能声明,但是要定义子类中该功能特有内容是,就保留覆盖操作完成。
4.4.3.1 实例
class Test1 {
public static void main(String[] args) {
zi a = new zi();
a.show();
}
}
class fu {
void show(){
System.out.println("Hello");
}
}
class zi extends fu {
void show() {
System.out.println("1");
System.out.println("2");
System.out.println("3");
super.show();
}
}
4.4.4 Object类
Java中,每个类有且仅有一个父类,如果一个类在定义是没有显式的声明继承另一个类,并不意味着该类没有父类,此时该类默认继承Object(上帝)类。
4.5 子类的实例化过程
4.5.1 子父类中构造函数的特点
问题:在子类构造对象时,发现访问子类构造函数时,父类也运行了,为什么?
原因:在子类的构造函数中的第一行有一个默认的隐式语句:super()
4.5.2 子类的实例化过程
子类中所有的构造函数默认都会访问父类中的空参数的构造函数。
一个对象实例化过程: Person p = new Person();
1. JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下).
2. 在堆内存中的开辟空间,分配地址。
3. 并在对象空间中,对对象中的属性进行默认初始化。
4. 调用对应的构造函数进行初始化。
5. 在构造函数中,第一行会先到调用父类中构造函数进行初始化。
6. 父类初始化完毕后,在对子类的属性进行显示初始化。
7. 在进行子类构造函数的特定初始化。
8. 初始化完毕后,将地址值赋值给引用变量.
4.5.2.1 子类实例化过程中为什么会访问父类中的构造函数?
因为子类继承了父类,获取到了父类中的内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。所以子类在构造对象时,必须访问父类中对构造函数。为了完成这个必须的动作,就在子类的构造函数中加入了super语句。如果父类中没有定义空参数构造函数,那么子类的构造函数必须用super明确要调用父类中的那个构造函数。同时子类的构造函数中如果使用this调用了本类构造函数时,那么super就没有了,因为super和this都只能定义在第一行,所以只能有一个,但可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。
注意:super语句必须要定义在子类构造函数的第一行,因为父类的初始化动作要先完成
最终抽取到最顶层的object (上帝)
4.5.2.2 子类实例化过程示例
class Fu {
Fu() {
super();
show();
return;
}
void show() {
System.out.println("fu show");
}
}
class Zi extends Fu {
int num = 8;
Zi() {
super();
//-->通过super初始化父类内容时,子类的成员变量并未显示初始化。等super()父类初始化完毕后,
//才进行子类的成员变量显示初始化。
System.out.println("zi cons run...."+num);
return;
}
void show() {
System.out.println("zi show..."+num);
}
}
class ExtendsDemo5 {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
4.5.2.3 子类实例化过程示例图解
4.6 final关键字
4.6.1 extends的弊端(为什么使用)
打破封装性,也就是可能会有恶意继承
class Fu {
public void show(){
//调用底层资源
}
}
class Zi extends Fu {
public void show(){
System.out.println("小样功能被我干掉了");
}
}
4.6.2 final 可修饰的对象
类、方法、变量
4.6.3 final 的作用
1. final修饰的类不可以被继承。
2. final修饰的方法不可以被覆盖。
3. final修饰的变量是一个常量,只能赋值一次。
常量写法规范:常量所有字母都大写,多个单词,中间用_连接。
注意:为什么要用final修饰变量?
其实在程序如果一个数据是固定的,那么直接使用这个数据就可以了,但是这样阅读性差,所以它该数据起个名称。而且这个变量名称的值不能变化,所以加上final固定。比如:π
4.6.4 final 修饰变量赋值问题
final修饰成员变量,必须初始化,初始化有两种
1. 显示初始化: final int NUM = 15;
2. 构造函数初始化:final 某类 NUM;//通过构造函数初始化(以验证,但觉多余)
但是不能两个一起初始化
一般定义把常量定义成final还需要定义成static的,为什么?定义成public static final int NUM = 15;//全局常量
4.7 抽象类
4.7.1 抽象类概述
抽象:抽象就是从多个事物中将共性的,本质的内容抽取出来。
抽象类: Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象类的由来:多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
4.7.2 抽象类的特点
1. 抽象方法一定在抽象类中
2. 抽象方法和抽象类都必须被abstract关键字修饰
3. 抽象类不可以用new创建对象,因为调用抽象方法没意义
4. 抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用; 如果子类只覆盖了部分的抽象方法,那么该子类还是一个抽象类。
5. 抽象类中可以有抽象方法也可以有非抽象方法
6. 抽象类和一般类没有太大的不同:该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂得东西。这些不确定的部分,也是该事物的功能,需要明确出现。但是无法定义主体。
7. 抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法; 抽象类不可以实例化。
特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
4.7.3 抽象类的细节
1. 抽象类中有构造函数吗?
答:有,用于给子类对象进行初始化。
2. 抽象类可以不定义抽象方法吗?
答:可以的。但是很少见,目的就是不让该类创建对象。AWT的适配器对象就
是这种类。
通常这个类中的方法有方法体,但是却没有内容。
例如:
abstract class Demo {
void show1()
{}
void show2()
{}
}
3. 抽象关键字不可以和那些关键字共存?
答:private 不行
抽象方法要被覆盖,若私有化子类则无法覆盖
static 不行
若成员变静态,不需要对象可直接调用,但抽象方法没有具体方法体,运行无意义。
final 不行
final和abstract本就是两个水火不容的关键字,一个是不能被覆盖,一个是要被覆盖,两个冲突。
4. 抽象类和一般类的异同点。
答:
相同点:
抽象类和一般类都是用来描述事物的,都在内部定了成员。
不同:
(1) 一般类有足够的信息描述事物;抽象类描述事物的信息有可能不足。
(2) 一般类中不能定义抽象方法,只能定非抽象方法;抽象类中可定义抽象方法,同时也可以定义非抽象方法。
(3) 一般类可以被实例化;抽象类不可以被实例化。
5. 抽象类一定是个父类吗?
答:是的。因为需要子类覆盖其方法后才可以对子类实例化。
4.7.3 什么时候定义抽象类
当我们分析事物时,对对象进行描述时,其实就不断把对象中的共性内容向上抽取.在抽取过程中,发现对象具备相同的功能,但是功能的细节不同.这时在定义类时,该功能是没有具体实现的,是由具体的对象来完成的.那么该功能就是抽象的。
4.7.4 抽象类举例代码讲解
雇员示例:
需求:
公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
对给出需求进行数据建模。
分析:
在这个问题领域中,先找出涉及的对象,通过名词提炼法。
程序员:
属性:姓名,工号,薪水、
行为:工作。
经理:
属性:姓名,工号,薪水,奖金。
行为:工作。
程序员和经理不存在着直接继承关系,但是程序员和经理却具有共性内容。可以进行抽取。因为他们都是公司的雇员,可以将程序员和经理进行抽取.建立体系。
代码:
class AbstractTest {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
//描述雇员。
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 abstract void work();
}
//描述程序员。
class Programmer extends Employee {
Programmer(String name,String id,double pay) {
super(name,id,pay);
}
public void work() {
System.out.println("code...");
}
}
//描述经理。
class Manager extends Employee {
private int bonus;
Manager(String name,String id,double pay,int bonus) {
super(name,id,pay);
this.bonus = bonus;
}
public void work() {
System.out.println("manage");
}
}
4.8 接口
4.8.1 接口概述
概述:
当一个抽象类中的方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口 interface。(表象上接口是一种特殊的抽象类,但与抽象类有所不同。)
接口是抽象方法和常量值的集合。
对于接口当中常见的成员:而且这些成员都有固定的修饰符。(不写系统会帮你加,但是阅读性差。)
全局常量: public static final
抽象方法:public abstract
由此得出结论,接口中的成员都是公共的权限。(权限都是最大的)
接口举例:
interface Demo {
public static final int ID = 1;
public abstract void start();
public abstract void run();
public abstract void stop();
}
4.8.2 接口的实现
类与类之间是继承关系,类与接口直接是实现关系。
如:
interface Demo {
public static final int NUM = 4;
public abstract void show1();
public abstract void show2();
}
class DemoImpl implements /*实现*/Demo {
public void show1() {};
public void show2() {};
}
注意:接口不可以实例化。
只能由实现了接口的子类并覆盖了接口中所有的抽象方法后,该子类才可以实例化。
否则,这个子类就是一个抽象类。
说明:
在java中不直接支持多继承,因为会出现调用的不确定性。所以java将多继承机制进行改良,在java中变成了多实现。一个类可以实现多个接口。
如:
interface A {
public void show();
}
interface Z {
public int add(int a,int b);
}
class Test implements A,Z { //多实现
public int add(int a,int b) {
return a+b+3;
}
public void show(){}
}
4.8.3 接口-细节
一个类在继承另一个类的同时,还可以实现多个接口。接口的出现避免了单继承的局限性。
类与类是继承的关系,接口和类是实现的关系,接口和接口是继承关系,而且接口和接口之间是多继承关系。(因为接口没有方法体,而类有,所以类没有办法多继承)
如:
interface CC {
void show();
}
interface MM {
void method();
}
interface QQ extends CC,MM { //接口与接口之间是继承关系,而且接口可以多继承。
void function();
}
class WW implements QQ { //覆盖3个方法。
public void show(){}
public void method(){}
public void function(){}
}
4.8.4 接口的特点
1. 接口是对外暴露的规则。
2. 接口是程序的功能扩展。
3. 接口的出现降低耦合性。(实现了模块化开发,定义好规则,每个人实现自己的模块,大大提高了开发效率)
4. 接口可以用来多实现。
5. 多个无关的类可以实现同一个接口.
6. 一个类可以实现多个相互直接没有关系的接口
7. 与继承关系类似,接口与实现类之间存在多态性
另一版本:
1. 一个类可以实现多个接口
2. 一个接口可以被多个类实现
3. 一个接口可以继承多个接口
4. 一个接口中只能定义常量和抽象方法
4.8.5 接口与抽象类
4.8.6 接口的应用
/*
笔记本电脑使用。
为了扩展笔记本的功能,但日后出现什么功能设备不知道。
定义一个规则,只要日后出现的设备都符合这个规则就可以了。
规则在java中就是接口。
*/
// 主类,为PC
class Pc {
public static void main(String[] args) {
//功能扩展了,调用useUSB功能,新建一个Upan,即为插上Upan
useUSB(new Upan());
useUSB(new Mouse());
}
//使用规则,接口类型的引用,用于接收(指向)接口类的子对象。
public static void useUSB(USB U) { // U是一个接口类型变量
if (U != null) {
U.open();
U.close();
}
}
}
// 定义一个暴漏的规则,即USB的插口,拥有打开和关闭的功能,日后出新设备只要满足这个规则就能使用。
interface USB{
public void open();
public void close();
}
// 定义一个类,对应优盘,现实暴漏的规则,覆盖了打开和关闭的功能,意为打开优盘,关闭优盘。
class Upan implements USB {
public void open() {
System.out.println("Upan open");
}
public void close(){
System.out.println("Upan close");
}
}
class Mouse implements USB {
public void open() {
System.out.println("Mouse open");
}
public void close(){
System.out.println("Mouse close");
}
}
4.9 多态
4.9.1 多态概述
多态定义:某一类事物的多种存在形态。
如: 猫x = new 猫();
动物 x = new 猫(); // 一个对象,两种形态。
猫这类事物既具备着猫的形态,又具备着动物的形态,这就是对象的多态性。
简单说:就是一个对象对应着不同的类型。
4.9.2 多态在代码中的体现
父类或者接口的引用指向其子类的对象。(使用父类类型的变量引用子类对象)
例:
class DuoTaiDemo {
public static void main(String[] args) {
method(new Dog());
method(new Cat());
method(new Pig());
}
public static void method(Animal a) { //Animal a = new Dog();
a.eat();
}
}
abstract class Animal {
abstract void eat();
}
class Dog extends Animal {
void eat() {
System.out.println("啃骨头");
}
void lookHome() {
System.out.println("看家");
}
}
class Cat extends Animal {
void eat() {
System.out.println("吃鱼");
}
void catchMouse() {
System.out.println("抓老鼠");
}
}
class Pig extends Animal {
void eat() {
System.out.println("饲料");
}
void gongDi() {
System.out.println("拱地");
}
}
4.9.3 多态的好处
提高了代码的扩展性,前期定义的代码可以使用后期的内容。
4.9.4 多态的弊端和前提
弊端:
前期定义的内容不能使用(调用)后期子类的特有内容。
多态的前提:
1. 必须有关系:继承、实现。
2. 要有覆盖。
4.9.5 多态 转型一
例如:
Animal a = new Cat();
自动类型提升,猫对象提升了动物类型。但是特有功能无法访问,作用就是:①限制对特有功能的访问 ②提高了扩展性。
专业讲叫:向上转型
如果还想使用具体动物猫的特有功能,可以将该对象进行向下转型。
如:
Cat c = (Cat)a; // 向下转型的目的是为了使用子类中的特有方法。
c.eat();
c.catchMouse();
注意:
对于转型,自始自终都是子类对象在做着类型变化。
4.9.6 多态 转型二
实例:毕老师和毕姥爷的故事。
class 毕姥爷 {
void 讲课() {
System.out.println("管理");
}
void 钓鱼() {
System.out.println("钓鱼");
}
}
class 毕老师 extends 毕姥爷 {
void 讲课() {
System.out.println("Java");
}
void 看电影() {
System.out.println("看电影");
}
}
class DuoTaiDemo2 {
public static void main(String[] args) {
// 毕老师 x = new 毕老师();
// x.讲课();
// x.看电影();
毕姥爷 x = new 毕老师();
x.讲课();
x.钓鱼();
毕老师 y = (毕老师)x; //ClassCastException
y.看电影();
}
}
4.9.7 类型判断 instanceof
格式:
对象(或对象引用变量) instanceof 类或接口
输出是布尔型
实例:
public static void method(Animal a)//Animal a = new Dog(); {
a.eat();
if(a instanceof Cat) {
//instanceof:于判断对象的具体类型。只能用于引用数据类型判断
//通常在向下转型前用于健壮性的判断。
Cat c = (Cat)a;
c.catchMouse();
}
else if(a instanceof Dog) {
Dog d = (Dog)a;
d.lookHome();
}
Else {
...
}
}
4.9.8 多态时成员的特点
4.9.8.1 多态时成员变量的特点
1. 编译时:参考引用型变量所属的类中的是否有调用的成员变量,有,编译通过,没有,编译失败。
2. 运行时:参考引用型变量所属的类中的是否有调用的成员变量,并运行该所属类中的成员变量。
3. 简单说:编译和运行都参考等号的左边。
4. 作为了解。
4.9.8.2 多态时成员函数(非静态)的特点(重点)
动态绑定:
是指“在执行期间”(而非编译期间)判断所引用对象的实际类型,根据实际类型调用相应的方法。
1. 编译时:参考引用型变量所属的类中的是否有调用的函数。有,编译通过,没有,编译失败。
2. 运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。
因为成员函数存在覆盖特性。
4.9.8.3 多态时静态函数的特点
1. 编译时:参考引用型变量所属的类中的是否有调用的静态方法。
2. 运行时:参考引用型变量所属的类中的是否有调用的静态方法。
简单说,编译和运行都看左边。
其实对于静态方法,是不需要对象的。直接用类名调用即可(静态绑定)。
/*实例day08-DuoTaiDemo3*/
4.9.8.4 小结
除了成员函数(非静态),编译看左,运行看右;
其余一律看左!
4.10 内部类
4.10.1 内部类的概念
概念:
将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,
嵌套类)。
分类:
1,成员内部类2,静态内部类3,局部内部类4,匿名内部类
访问特点:
1. 内部类可以直接访问外部类中的成员,包括私有成员。
//外部名.this
2. 而外部类要访问内部类中的成员必须要建立内部类的对象。
使用:
一般用于类的设计,分析事物是,发现事物描述中还有事物,而且这个事
物还存在被访问被描述的内容,这时就把还有的事物定义成内部类来描述。
注意:
如果内部类定义了静态成员,该内部类必须要被静态修饰。
4.10.2 内部类的访问方式
修饰符:
可以被:Private 、 static修饰。
注意:被static修饰的内部类只能访问外部类中的静态成员。
访问方式:
内部类访问外部类,内部类可以直接访问外部类,包括私有成员,因为内部类
拥有外部类的引用是类名.this
外部类访问内部类,外部类访问外部类的成员,必须要建立内部类的对象
格式:外部类名.内部类名 = 外部类对象.内部类对象;
Outer.Inner oi = new Outer().new Inner();
//outer代表外部类,Inner代表内部类
//直接访问外部类中的内部类中的成员。
Outer.Inner in = new Outer().new Inner();
in.show();
//如果内部类是静态的。相当于一个外部类
Outer.Inner in = new Outer.Inner();
in.show();
//如果内部类是静态的,成员是静态的。
Outer.Inner.function();
4.10.3 内部类细节
为什么内部类能直接访问外部类中成员呢?
答:那是因为内部类持有了外部类的引用。 外部类名.this
4.10.4 内部类的共性
1. 内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类命和$符号。
2. 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。
4.10.5 成员内部类
解释:
在一个外部类中有成员变量,和成员方法,那么成员内部类就是把整个一个类当成了外部类的成员了。
修饰符:
可用的修饰符有:final、abstract、public、private、protected和static等。
一旦用static修饰内部类,它就变成静态内部类了。
位置:
在类中并且在方法外定义的类。
成员内部类创建对象方式:
外部类名.内部类名 对象名 = 外部类对象.内部类对象
成员内部类之所以可以直接访问外部类的成员,那是因为内部类中都持有一个外部类对象的引用:外部类名.this
4.10.6 静态内部类
解释:
静态内部类就是给成员内部类加上static这个修饰符
位置:
在类中并且在方法外定义的类.
在外部类中访问静态内部类的方式有两种可能:
第一种:在外部类中访问静态内部类中的非静态成员,通过创建对象访问。
外部类名.内部类名 对象名 = 外部类名.内部类对象
第二种:在外部类中访问静态内部类中的静态成员
可以通过上一种
也可以
外部类名.内部类名.成员
我们把静态内部类看成一个类中的成员,既然是静态修饰的,就可以用类名.调用
注意:
如果成员内部类用static修饰就出现了访问局限性和静态方法是一个道理,就不能访问外部类的非静态成员了
静态内部类之所以可以直接访问外部类中的静态成员,其实是持有外部类名
4.10.7 局部内部类(方法内部类)
位置:定义在方法中的类
注意:
第一
方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
第二
方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
第三:
与成员内部类不同,方法内部类更像一个局部变量。
可以用于修饰方法内部类的只有final和abstract。
第四:
静态方法内的方法内部类
静态方法是没有this引用的,因此在静态方法内的内部类遭受同样的待遇,即:只能访问外部类的静态成员。
4.10.7 匿名内部类
4.10.7.1 匿名内部类概念
概念:就是内部类的简写格式。
前提:内部类必须继承或者实现一个外部类或者接口。
作用:可以在外部类没有创建对象的情况下实例化。
格式:
new 父类(参数列表) 或者 父接口(){
// 匿名内部类实现部分
}
实质:其实就是一个匿名子类对象!做了个简化封装对象。
4.10.7.2 匿名内部类的应用
a,继承式的匿名内部类
b,接口式(也可以叫实现式的,名字无所谓)的匿名内部类
c,参数式的匿名内部类
多加一句:我的理解,其实与其说是匿名内部类,我个人认为说是匿名对象更确切一点(个人理解,仅供参考);
4.10.7.3 匿名内部类细节
1. 匿名内部类不能是抽象类,因为在定义匿名内部类的同时就在创建该类的实例对象。
2. 匿名内部类不能定义构造方法,因为匿名内部类没有类名,所以无法定义构造方法。但匿名内部类可以定义实例初始化代码块,通过实例初始化代码块来完成构造方法所需要完成的初始化工作。
4.11 模版方法模式(Template)
4.11.1 模版方法模式概述
在定义功能时,功能的一部分是确定的不允许更改的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去。由该类的子类去实现。
4.11.2 模版方法模式举例
要求:计算指定代码运行的时间
代码:
class TemplateTest {
public static void main(String[] args) {
GT a = new GT();
a.gettime();
}
}
abstract class GetTime{
public final void gettime() {
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
abstract public void code();
}
class GT extends GetTime{
public void code(){
for (int i = 1; i < 10000 ; i++){
System.out.print("*");
}
}
}
4.12异常
4.12.1 异常概述
异常:是在运行时期发生的不正常情况。
其实就是Java通过面向对象的思想将问题封装成了对象,用异常类对其行
为进行描述。不同的类用不同的类进行具体的描述,比如角标越界、空指针等等。
异常类:对发生的异常以类的形式进行封装。
在Java中用类的形式对不正常的情况进行了描述和封装。描述不正常的情
况的类就成为异常类。
以前正常流程代码和问题处理代码相结合,现在将正常流程代码和问题处
理代码相分离,提高了代码的阅读性。
异常体系:
问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常
体系。
最终问题(不正常情况)就分成了两大类:
Throwable :
|--1, 一般不可处理的的。Error
| 特点:由JVM抛出的严重性问的题,这种问题反生一般不针对性处理,
| 直接修改程序
|--2, 可以处理的。Exception
该体系的特点:子类的后缀名都是其父类名作为后缀,阅读性很强。
不论是Error还是异常,还是异常,都是问题,问题发生就应该可以抛出,让调用者知道并处理,该体系的特点就在于Throwable及其所有的子类都具有可抛性。
问题:可抛性到底指的是什么呢?怎么体现可抛性呢?
答:其实是通过两个关键字来体现的:throws、throw
凡是可以被这两个关键字所操作的类和对象都具备可抛性。
4.12.2 异常抛出
4.12.2.1 异常对象的抛出:throw
用于抛出异常对象。
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
4.12.2.2 异常类的抛出:throws
throws用于标识函数暴露出的异常。
定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在函数上标识。
异常的分类:
1,编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。 这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。
2,编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。
所以自定义异常时,要么继承Exception。要么继承RuntimeException。
throws 和throw的区别
1,throws使用在函数上,throw使用在函数内。
2,throws抛出的是异常类,可以抛出多个,用逗号隔开,throw抛出的是异常对象。
4.12.3 异常捕捉
异常处理的捕捉形式:这是可以对异常进行针对性处理的方式。
具体格式是:
try {
//需要被检测异常的代码。
}
catch(异常类 变量) { //该变量用于接收发生的异常对象
//处理异常的代码。
}
finally {
//一定会被执行的代码。
}
实例:class FuShuIndexException extends Exception
{
FuShuIndexException()
{}
FuShuIndexException(String msg)
{
super(msg);
}
}
class Demo
{
public int method(int[] arr,int index)//throws NullPointerException,FuShuIndexException
{
if(arr==null)
throw new NullPointerException("没有任何数组实体");
if(index<0)
throw new FuShuIndexException();
return arr[index];
}
}
class ExceptionDemo4
{
public static void main(String[] args)
{
int[] arr = new int[3];
Demo d = new Demo();
try
{
int num = d.method(null,-1);
System.out.println("num="+num);
}
catch(NullPointerException e)
{
System.out.println(e.toString());
}
catch (FuShuIndexException e)
{
System.out.println("message:"+e.getMessage());
System.out.println("string:"+e.toString());
e.printStackTrace();//jvm默认的异常处理机制就是调用异常对象的这个方法。
System.out.println("负数角标异常!!!!");
}
/*
catch(Exception e)//多catch父类的catch放在最下面。
{
}
*/
System.out.println("over");
}
}
多catch父类的catch放在最下面。
4.12.4 自定义异常
自定义类继承Exception或者其子类(RuntimeException)
class MyException extends Exception{
MyException(){}
MyException(Stirng message){
super(message); //将信息传递给父类,调用父类封装好的构造方法
}
}
class Student {
public void giveAge(int age) throws MyException {
if(age>40 || age<0) {
//throw new MyExcetpion("建议不学了");
MyExcepiont my = new MyExcetpion("建议不学了");
throw my;
}
else {
System.out.println("可以学习Java");
}
}
}
4.12.5 异常处理的原则
1,函数内容如果抛出需要检测的异常,那么函数上必须要声明。否则必须在函数内用try catch捕捉,否则编译失败。
2,如果调用到了声明异常的函数,要么try catch要么throws,否则编译失败。
3,什么时候catch,什么时候throws 呢?功能内容可以解决,用catch。解决不了,用throws告诉调用者,由调用者解决 。
4,一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。内部又几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。
try catch finally 代码块组合特点:
1,try catch finally
2, try catch(多个)当没有必要资源需要释放时,可以不用定义finally。
3, try finally 异常无法直接catch处理,但是资源需要关闭。
4.13 包
一.package
1.定义包
如何去定义包?
在程序的第一行写package这个关键字后面跟的是域名
建议域名倒着写,而且要求小写
2.编译带包的类
javac - d .(当前目录)或指定的目录 类名.java
3.默认包
javac 类名.java
4.运行带包的类
java 包名.类名
5.访问权限
6.导入其他包中的类
导包通过关键字import,导包到底是导入包还是导入类?
其实导入的是包中的类
星式导入:他是会用到那个就导入哪个类,导入先优先本包中的,然后再去指定包中找
类名导入:直接在指定的包中找,建议在开发的时候,写具体的导入类
7.有包类和无包类之间的访问问题
1,有包的类是否可以访问无包的类?不可以
2,无包的类是否可以访问有包的类?可以,直接导入就行
8.重点
写类的时候要加上包名
类要public, 并且类名和文件名一致
导入类要写类名
编译用-d, 运行带包名
二.jar
1.jar是什么
我们平时的压缩文件有.zip.rar
jar是java中的压缩包
2.怎么打jar包
jar cvf jar包的名字要压缩的包(放的也是.class文件)或者.class文件
3.怎么运行jar包
a,我们打好jar包中把MANIFEST.MF拖出来,然后用editplus打开,加上一行
Main-Class: 主函数的全类名(包名.类名)
b,再将这个MANIFEST.MF拖回去,替换
c,java -jar jar包的名字
4.怎么使用jar包中的类
可以将jar包设置到classpath路径中
这个路径是绝对路径+jar包的名字