----------------------ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
一.抽象类
1.抽象类的概念
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有
包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
比如,在一个图形编辑软件的分析设计过程中,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它
们又都属于形状这样一个概念,形状这个概念在问题领域并不是直接存在的,它就是一个抽象概念。而正是因为抽象的概念在问题领
域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。通常在编程语句中用abstract修饰的类是抽象类。在
Java中,含有抽象方法的类称为抽象类,不能用来生成对象。抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要
用来进行类型隐藏和充当全局变量的角色。
抽象类的声明格式为
abstract class A {
···
}
2.抽象类的特点
1)abstract类中可以有abstract方法
与普通的类相比,abstract类可以有abstract方法。对于abstract方法,只允许声明,不允许实现,而且不允许使用final修饰abstract方
法。下面的AbstractDemo1类中的min()方法是abstract方法。
- abstract class AbstractDemo1 {
- abstract int min(int x,int y); //抽象方法不允许实现
- int max(int x,int y) { //非抽象方法必须实现
- return x>y?x:y;
- }
- }
注意:abstract类也可以没有abstract方法。
2)abstract类不能用new运算符创建对象
对于abstract类,不能使用new运算符创建该类的对象,需产生其子类,由子类创建对象,如果一个类是abstract类的子类,它必须具体实现父类的abstract方法,这就是为什么不允许使用final修饰abstract方法的原因。在下面的代码示例,使用了abstract类。
- <span style="font-size:12px;">public class AbstractDemo2 {
- /**使用抽象类,并为之创建对象,调用其抽象方法
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- A a; //A a = new A();是非法的,因为abstract类不能用new运算符创建对象
- B b = new B();
- int max = b.max(12, 13); //调用最大值方法算最大值
- int min = b.min(12, 13); //调用最小值方法算最小值
- System.out.println("12和13的最大值是" + max); //输出结果是:12和13的最大值是13
- System.out.println("12和13的最小值是" + min); //输出结果是:12和13的最小值是12
- }
- }
- abstract class A {
- abstract int min(int x, int y); //定义抽象方法算最小值
- int max(int x, int y) { //定义抽象方法算最大值
- return x > y ? x : y; //返回最大值
- }
- }
- class B extends A {
- int min(int x, int y) { //abstract方法必须实现
- return x < y ? x : y; //返回最小值
- }
- }</span>
在下面的代码示例中,有一个abstract的“图形”类,图形类要求其子类都必须有具有计算面积的功能。
- public class AbstractDemo3 {
- /**创建一个abstract的图形类,要求子类必须具有计算面积的功能
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- 梯形 t = new 梯形(2.0, 5.2, 12.3);
- 圆 y = new 圆(5.6);
- System.out.println("该梯形的面积是:" + t.求面积()); //输出结果是:该梯形的面积是:44.28
- System.out.println("该圆的面积是:" + y.求面积()); //输出结果是:该圆的面积是:98.52034561375999
- }
- }
- abstract class 图形 { //定义抽象类
- public abstract double 求面积(); //定义抽象方法求面积();这样可以要求子类必须实现
- }
- class 梯形 extends 图形 { //定义梯形类继承图形类
- double a, b, h;
- 梯形(double a, double b, double h){
- this.a = a;
- this.b = b;
- this.h = h;
- }
- public double 求面积() { //实现图形的抽象方法
- return ((1/2.0) * (a + b) * h);
- }
- }
- class 圆 extends 图形 { //定义圆类继承图形类
- double r;
- final double PI = 3.1415926535;
- 圆(double r) {
- this.r = r;
- }
- public double 求面积() { //实现图形的抽象方法
- return PI * r * r;
- }
- }
3.比较区别
与具体类比较
1)抽象类不能直接实例化,并且对抽象类使用new运算符会导致编译时错误。虽然一些变量和值在编译时的类型可以是抽象的,但是这样的变量和值必须或者为 null,或者含有对非抽象类的实例的引用(此非抽象类是从抽象类派生的)。
2)允许(但不要求)抽象类包含抽象成员。
3)抽象类不能被密封。
与接口比较
抽象类表示该类中可能已经有一些方法的具体定义,但是接口就仅仅只能定义各个方法的界面(方法名,参数列表,返回类型),并不关心具体细节。
接口是引用类型的,和抽象类的相似之处有三点:
1)不能实例化;
2)包含未实现的方法声明;
3)派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)。
抽象类与接口紧密相关。然而接口又比抽象类更抽象,这主要体现在它们的差别上:
1)类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承,从抽象类派生的类仍可实现接口,从而得出接口是用来解决多重继承问题的。
2)抽象类当中可以存在非抽象的方法,可接口不能,且它里面的方法只是一个声明必须用public来修饰没有具体实现的方法。
3)抽象类中的成员变量可以被不同的修饰符来修饰,可接口中的成员变量默认的都是静态常量(static final)。
4)抽象类是对象的抽象,然而接口是一种行为规范。
抽象类里面可以有非抽象方法但接口里只能有抽象方法 声明方法的存在而不去实现它的类被叫做抽像类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽像类,并让它指向具体子类的一个实例。不能有抽像构造函数或抽像静态方法。Abstract 类的子类为它们父类中的所有抽像方法提供实现,否则它们也是抽像类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。接口(interface)是抽像类的变体。在接口中,所有方法都是抽像的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽像的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对像上调用接口的方法。由于有抽像类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。
4.抽象类的用法要求总结
1)abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
2)在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
3)abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。
4)实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
5)接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。
6)抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
7)接口中的方法默认都是 public,abstract 类型的。
二.内部类
1)非静态成员内部类
A:定义,:在一个外部类中有成员变量和成员方法,那么成员内部类就是把整个一个类当成了外部类的成员对待了
B: 访问方式:内部类访问外部类,内部类可以直接访问外部类,包括私有成员,因为内部类拥有外部类的引用是类名.this。外部类访问内部类,外部类访问外部类的成员,必须要建立内部类的对象
格式:外部类名.内部类名 = 外部类对象.内部类对象;
Outer.Inner oi = new Outer().new Inner();//outer代表外部类,Inner代表内部类
C:存放位置:在外部类里,在外部类的成员方法外.
D:修饰符:final、abstract、public、private、protected和static等,那么被static修饰的就是下面所说的静态内部类.
- <span style="font-size:12px;">public class InnerDemo1 {
- /**展示非静态成员内部类的创建方式
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Outer.Inner oi = new Outer().new Inner(); //非静态成员内部类的创建方式
- oi.show(); //调用内部类的show()方法
- }
- }
- class Outer {
- private String name = "张三"; //设置私有变量
- class Inner {
- public void show() {
- System.out.println(Outer.this.name); //读取name变量并打印
- }
- }
- }</span>
A:定义,就是在成员内部类的基础上加上static
B:格式:外部类名.内部类名 = 外部类名.内部类对象;
Outer.Inner oi = new Outer.Inner();
C:存放位置:和成员内部类一样,就是多了个static
- public class InnerDemo2 {
- /**展示静态内部类的创建方式
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Outer.Inner oi1 = new Outer.Inner(); //静态内部类的创建方式
- oi1.show(); //输出结果是:Hello World!
- Outer.Inner2 oi2 = new Outer.Inner2();
- oi2.show2(); //输出结果是:Hello World!2
- }
- }
- class Outer {
- static class Inner {
- public void show() {
- System.out.println("Hello World!");
- }
- }
- static class Inner2 {
- public static void show2() {
- System.out.println("Hello World!2"); //注意不要在非静态的内部类中定义静态的方法
- }
- }
- }
A:定义,在外部类成员方法中定义的内部类,他更像局部变量
B: 注意:
第一:方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
第二:方法内部类对象不能使用该内部类所在方法的非final局部变量。因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。 正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
第三:方法内部类的修饰符。与成员内部类不同,方法内部类更像一个局部变量 。可以用于修饰方法内部类的只有final和abstract。
第四:静态方法内的方法内部类。静态方法是没有this引用的,因此在静态方法内的内部类遭受同样的待遇,即:只能访问外部类的静态成员。
(4)匿名类:匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,也就是匿名类。这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。
A:前提:内部类可以继承或实现一个外部类或者接口。
B:格式为:new 外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容。)}
C:简单理解:就是建立一个带内容的外部类或者接口的子类的匿名对象。
D: a,继承式的匿名类
b,接口式(也可以叫实现式的,名字无所谓)的匿名类
c,参数式的匿名类
代码示例:
- public class InnerDemo3 {
- /**匿名内部类的使用
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Outer o = new Outer();
- o.print();
- }
- }
- class Demo {
- public void show() {
- System.out.println("Hello World!");
- }
- }
- class Outer {
- public void print() { //匿名内部类
- new Demo() {
- }.show();
- }
- }
匿名内部类的注意事项
注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
在使用匿名内部类时,要记住以下几个原则:
1)匿名内部类不能有构造方法。
2)匿名内部类不能定义任何静态成员、方法和类。
3)匿名内部类不能是public,protected,private,static。
4)只能创建匿名内部类的一个实例。
5)一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
6)因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
7)内部类只能访问外部类的静态变量或静态方法。
8)匿名类和内部类中的中的this :有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名
匿名内部类的作用
Java的内部类和C++中的嵌套类有本质的不同:C++的嵌套类没有指向包装类的句柄。仅仅表达一个封装的概念;但是Java的内部类不同,它可以访问包装类的成员(这表示它拥有指向包装类的句柄)。
匿名内部类是内部类的一种简化写法:return new Wrapper {
...
};
等价于:Wrapped extends Wrapper {
...
}
return new Wrapped();
难道匿名内部类就只这一点作用吗?考虑一下下面的例子
- interface ICount {
- int count();
- }
- class Parent {
- int i = 0;
- int count() {
- return i++;
- }
- }
有一个类Child,它既想继承Parent的count()方法,又想实现ICount接口中的count方法,这个时候怎么办呢?内部类就可以大显身手了:
- class Child extends Parent {
- ICount getCount() {
- return new ICount {
- int i = 0;
- int count() {
- return (i *= 2);
- }
- }
- }
- }