夜雪天狼
学习笔记
技术博文
转载备份
心灵鸡汤
目录
面向对象思想
发布者:caijw
阅读量:62240
发布时间:2013-06-21 02:07:42
# 面向对象 面向对象是Java语言区别c语言的一个重要特点,也是学好Java必须掌握的重要部分。 ## 理解面向对象 c语言是一门面向过程的语言,而向对象就是相对面向过程而言的 如把大象装进冰箱,c语言强调的是功能行为: 1. 打开冰箱 2. 把大象装进冰箱 3. 关闭冰箱 而Java面向对象的思想就是强调具有功能的对象:打开,存储,关闭这是冰箱的功能,所以把大象装进冰箱是在调用冰箱的三个功能:冰箱打开,冰箱存储,冰箱关闭 可以理解为过程是调用者亲自在做,而面向对象是调用者指挥着对象去做 面向对象和面向过程都是一种思想,面向对象是基于面向过程的 ## 面向对象的特点: 1. 是一种符合人们思考习惯的思想 2. 可以将复杂的事情简单化 3. 将程序员从执行者转换成了指挥者 4. 完成需求时: 1. 先要去找具有所需的功能的对象来用 2. 如果该对象不存在,那么创建一个具有所需功能的对象 3. 这样简化开发并提高复用 在Java的开发过程,其实就是不断的创建对象,使用对象,指挥对象做事情 设计的过程,其实就是在管理和维护对象之间的关系 面向对象的三个特征: * 封装(encapsulation) * 继承(inheritance) * 多态(polymorphism) ## 类与对象 ### 类与对象的关系 使用计算机语言就是不断的描述现实生活中的事物,而java中描述事物通过类的形式体现,类是具体事物的抽象,概念上的定义 生活当中描述事物无非就是描述事物的属性和行为。如:人的身高,体重等属性;讲话跑步等行为 Java则是用类class来描述对象的属性和行为。定义类其实就是在定义类中的成员(成员变量和成员函数) * 属性:对应类中的成员变量 * 行为:对应类中的成员函数 而对象即是该类事物实实在在存在的个体 ```java /** * 定义了一个以人为对象的类 * 其中人这个对象具有年龄的属性,还具有说话的功能 */ class Person { int age; private void speak() { System.out.println("my age is "+age); } } Person p = new Person(); p.age=20;//对对象属性的修改 p.speak();//调用对象的说话功能 ``` ### 成员变量和局部变量 之前我们用到的基本都是局部变量,现在我们将会使用成员变量。其实它两都是变量,规范写法上也没啥区别,都是标识符,但是在作用范围和内存中分配不同 区别: * 成员变量: * 成员变量定义在类中,在整个类中都可以被访问 * 成员变量随着对象的建立而建立,存在于对象所在的堆内存中 * 成员变量有默认初始化值 * 局部变量: * 局部变量只定义在局部范围内,如:函数内,语句内等 * 局部变量存在于栈内存中 * 作用的范围结束,变量空间会自动释放 * 局部变量没有默认初始化值 ### 匿名对象 匿名对象是对象的简化形式 匿名对象两种使用情况 1. 当对对象方法仅进行一次调用的时 2. 匿名对象可以作为实际参数进行传递 > 注:如果对一个对象进行多个成员调用,必须给这个对象起个名字 ## 对象的封装 是指隐藏对象的属性和实现细节,仅对外提供公共访问方式 好处 1. 将变化隔离 2. 便于使用 3. 提高重用性 4. 调高安全性 原则 1. 将不需要对外提供的内容都隐藏起来 2. 把属性都隐藏,提供公共方法对其访问 ## 构造函数 作用:给对象进行初始化 ### 特点 1. 函数名与类名相同 2. 不用定义返回值类型 3. 不可以写return语句 ### 空参构造函数 当一个类中没有定义构造函数时,那么系统就会默认给该类加入一个空参数的构造函数。当在类中自定义了构造函数后,默认的构造函数就没有了 ### 构造函数和一般函数 构造函数和一般函数在写法上有不同,在运行上也有不同 构造函数式在对象一建立就运行,给对象初始化,而一般方法是对象调用才执行,给是对象添加对象具备的功能 一个对象建立构造函数只运行一次,而一般方法可以被该对象调用多次 ### 什么时候定义构造函数? 当分析事物时,该事物一存在就应该具备一些特性或者行为,那么将这些内容定义在构造函数中 ### 构造代码块 作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数运行 构造代码块和构造函数的区别: 1. 构造代码块是给所有对象进行初始化 2. 构造函数是给对应的对象初始化 > 构造代码块中定义的是不同对象共性的初始化内容 ## this关键字 this代表它所在函数所属对象的引用,简单说:哪个对象在调用this所在的函数,this就代表哪个对象 ### this的应用 当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象 但凡本类功能内部使用到了本类对象,都用this表示 ### this语句 用于构造函数之间进行互相调用。如:this(name); this语句只能定义在构造函数的第一行。因为初始化要先执行 ### 对this的概括总结 this的两种用法: 1. 用于区分同名变量的情况,说的成员和局部同名的时候 2. 用于构造函数间调用 > 一般函数不能直接调用构造函数,因为this语句不能用在一般函数中,只能用在构造函数间 ## static关键字 static是一个修饰符,用于修饰成员(成员变量和成员函数)。当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。格式:类名.静态成员 ### static特点: 1. 随着类的加载而加载。也就是说:静态会随着类的消失而消失,说明它的生命周期最长 2. 优先于对象存在。明确一点:静态是先存在。对象是后存在 3. 被所有对象所共享 4. 可以直接被类名所调用 >由于静态成员可以直接被类名调用,因此静态成员变量又称为类变量。而非静态成员变量又被称为实例变量 ### 实例变量和类变量的区别: 存放位置: 1. 类变量随着类的加载而存在于方法区中 2. 实例变量随着对象的建立而存在于堆内存中 生命周期: 1. 类变量生命周期最长,随着类的消失而消失 2. 实例变量生命周期随着对象的消失而消失 ### 静态有利有弊 利处:对对象共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。可以直接被类名调用 弊端:生命周期过长。访问出现局限性(静态虽好,只能访问静态) ### 什么时候使用静态 从两方面下手: 因为静态修饰的内容有成员变量和函数 什么时候定义静态变量(类变量)呢? * 当对象中出现共享数据时,该数据被静态所修饰 * 对象中的特有数据要定义成非静态存在于堆内存中 什么时候定义静态函数呢? * 当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的 ### 静态使用注意事项 1. 静态方法只能访问静态成员,非静态方法既可以访问静态也可以访问非静态 2. 静态方法中不可以定义this,super关键字。因为静态优先于对象存在。所以静态方法中不可以出现this 3. 主函数是静态的 ### 静态代码块 特点:随着类的加载而执行,执行一次。并优先于主函数。用于给类进行初始化 ```java static { //静态代码块中的语句。 } ``` ## 对象的初始化过程 定义一个新对象都做了哪些事情? 1. 把类名.class文件加载到内存中 2. 执行该类中的static代码块,用以给该类进行初始化,如果有得话 3. 在堆内存中开辟空间,分配内存地址给对象 4. 在堆内存中建立对象的特有属性,并进行默认初始化 5. 对属性进行显示初始化 6. 对对象进行构造代码块初始化 7. 对对象进行对应构造函数初始化 8. 将内存地址赋给栈内存中的对象名变量 ## 主函数 主函数是一个特殊的函数。作为程序的入口,可以被JVM调用。 主函数的定义: * public:代表着该函数访问权限是最大的 * static:代表主函数随着类的加载就已经存在了 * void:主函数没有具体的返回值 * main:不是关键字,但是是一个特殊的单词,可以被JVM识别 * (String[] arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组 > 主函数是固定格式的:被JVM识别。是程序的入口 # 继承 ## 继承概述 继承是面向对象的一个重要特征: 当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可 这时,多个类可以称为子类,单独的这个类称为父类或者超类 例如:猫和豹子都是猫科动物,那么就可以说描述猫这个对象创建的类,是子类;而描述猫科动物这个对象创建的类,就是父类 这样类与类之间就存在了继承的关系。子类可以直接访问父类中的非私有的属性和行为。在代码中通过extends关键字表示继承关系 ```java class Son extends Father{}//这也是在代码中的书写格式 ``` > 注意:千万不要为了获取其他类中的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。这种所属关系的表示为is a,即某类是某类的一种 ## 继承特点 1. 提高了代码的复用性 2. 让类与类之间产生了关系,有了这个关系,提供了多态的前提 > 注:Java语言中,只支持单继承,不支持多继承 > 例:一个儿子只能有一个父亲 > 原因:因为类与类多继承的话,容易带来安全隐患 > 如:当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪个一个 > 但是Java保留了这种机制,并用另一种体现形式来完成。叫多实现 ## 继承体系 Java类中虽然不支持多继承,但可以多层继承,也就是一个继承体系:如儿子继承父亲,父亲继承爷爷等 ```java class A{} class B extends A{} class C extends B{} ``` 那么如何使用一个继承体系中的功能呢? 想要使用体系,先查体系中父类的描述,因为父类中定义的是该体系中的共性功能。通过了解共性功能,就可以知道该体系的基本功能。这样这个体系就可以基本使用了。 在具体调用时,要创建最子类的对象。 因为有可能父类不能创建对象。 创建子类对象可以使用更多的功能,包括基本的也包括特有的。 简单说就是:查阅父类功能,创建子类对象 ## 继承的应用 子父类出现后,类成员(变量,函数,构造函数)的特点 ### 变量 如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this;子类要访问父类中的同名变量,用super super的使用和this的使用几乎一致,且两者都存在于方法区中 this表示本来对象的引用 super表示父类对象的引用 ### 函数——覆盖 当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容 如同父类的函数被覆盖一样。这种情况是函数的另一个特性:重写(覆盖) 当子类继承父类,沿袭了父类的功能,到子类中。但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。子类同时具有父类方法中的内容时,可以用super.方法() 注: 1. 子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败 2. 静态只能覆盖静态 3. 父类中的私有方法不能被重写 重载与重写: * 重载:只看同名函数的参数列表 * 重写:子父类方法要一模一样 ### 构造函数 在对子类对象进行初始化时,父类的构造函数也会运行。那是因为子类的每一个构造函数默认第一行有一条隐式的语句super(); super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super(); 为什么子类一定要访问父类中的构造函数 因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数 如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定 > 注:super语句一定定义在子类构造函数中的第一行 子类的所有构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式super(); 当父类中没有空参数的构造函数时,子类必须手动通过supe语句或者this语句形式来指定要访问的构造函数 当然子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数 子类中至少会有一个构造函数会访问父类中的构造函数 > 为什么this()和super()不能在同一个构造函数中? > 因为它两不能在同一行,因为初始化动作要先做。在子类构造函数中必有一个this语句或者super语句 ## final关键字 继承的出现,打破了对象的封装性,使得子类可以随意复写父类中的功能 这也是继承的一大弊端。那么怎么解决这个问题呢?这里就引出了一个新的关键字——final(最终) final作为一个修饰符。具有以下特点: 1. 可以修饰类、函数、变量 2. 被final修饰的类不可以被继承,这样就可以避免被继承、被子类复写功能 3. 被final修饰的方法不可以被复写 4. 被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,又可以修饰局部变量 当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读 而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接 5. 内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量 # 抽象类 ## 抽象的定义 抽象就是从多个事物中将共性的,本质的内容抽取出来。例如:狼和狗共性都是犬科,犬科就是抽象出来的概念 ## 抽象类 Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类 抽象类的由来: 多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法 例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。 ## 抽象类的特点 1. 抽象类和抽象方法必须用abstract关键字来修饰 2. 抽象方法只有方法声明,没有方法体,定义在抽象类中 格式:修饰符abstract返回值类型 函数名(参数列表); 3. 抽象类不可以被实例化,也就是不可以用new创建对象。原因如下: 抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗 而且抽象类即使创建了对象,调用抽象方法也没有意义 4. 抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类 > 注:抽象类中可以有非抽象的方法 ## 抽象类与一般类的区别 1. 抽象类和一般类没有太大的不同。该如何描述事物,还是如何描述事物。只不过,该事物中出现了一些不知道具体内容的方法部分。这些不确定的部分,也是该事物的功能,需要明确出来,但是无法定义主体。通过抽象方法来表示 2. 抽象类比一般类多了个抽象函数。就是在类中可以定义抽象方法 3. 抽象类不可以实例化 4. 抽象类虽然不能创建对象,但是也有构造函数。供子类实例化调用 > 注: 1、被abstract修饰的函数不能同时被private、final、static修饰 原因: final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类 private:抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写 static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义 2、抽象有一个特殊的地方:抽象类中可以不定义抽象方法。这样做可以不让本来实例化,也可以用于模块设计 # 接口 ## 接口概述 接口,可以被认为是一个特殊的抽象类 当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示 接口使用interface来表示,子类中用implements实现。格式为: ```java interface 接口名{} 子类名 implements 接口名{} ``` 格式特点: 1. 接口中常见定义:常量,抽象方法 2. 接口中的成员都有固定修饰符 常量:public static final 方法:public abstract 3. 接口中的成员都是public的 在使用中,常量可以不写public static final,方法可以不写public abstract,编译时Java会自动添加这些修饰符,因为这是固定的格式修饰符 但为了方便阅读,通常我们都写上 ## 特点 1. 接口是对外暴露的规则 2. 接口是程序的功能扩展 3. 接口的出现降低耦合性 4. 接口可以用来多实现,这也是对多继承不支持的转换形式,java支持多实现 5. 类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口 6. 接口与接口之间可以有继承关系。而且可以多继承 > 注: 1、接口不可以创建对象的,因为有抽象方法。需要被子类实现(implements),子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类 2、实现多个接口时,接口中不可以有返回不同类型的同名抽象函数。这样子类实现时将不能复写 ## 接口与抽象类 共性:都是不断向上抽取出来的抽象的概念 区别: 1. 抽象类体现继承关系,一个类只能单继承;接口体现实现关系,一个类可以多实现。同时接口与接口之间有继承关系 2. 抽象类是继承,是 "is a "关系;接口是实现,是 "like a"关系 3. 抽象类中可以定义非抽象方法,供子类直接使用;接口的方法都是抽象,接口中的成员都有固定修饰符 4. 抽象类中可以私有变量或方法;接口中的常量和方法都是public修饰的权限 # 多态 多态可以理解为事物存在的多种体现形态 例:动物中猫,狗。猫这个对象对应的类型是猫类型,如:猫 x = new 猫(); 同时猫也是动物中的一种,也可以把猫称为动物。动物 y = new 猫(); 那么动物就是猫和狗具体事物中抽取出来的父类型。父类型引用指向了子类对象 ## 多态的体现 1. 父类的引用指向了自己子类的对象 2. 父类的引用也可以接收自己的子类对象 > 如:Animal a = new Cat();其中就将父类型的 a 引用指向了子类的对象 ## 多态的前提 1. 类与类之间必须有关系,要么继承,要么实现 2. 存在覆盖。父类中有方法被子类重写 ## 多态的利与弊 利:提高了程序的可扩展性和后期可以维护性 弊:虽然可以预先使用,但是只能访问父类中已有的功能,运行的是后期子类的功能内容。不能预先使用子类中定义的特有功能 ## 转型 子类对象被父类引用:子类对象在向上转型 将指向子类对象的父类应用转换成子类类型引用:向下转型 ```java // 毕姥爷和毕老师的故事: class 毕姥爷{} class 毕老师 extends 毕姥爷{} 毕姥爷 ly = new 毕老师();//毕老师向上转型为了毕姥爷。向上转型 毕老师 ls = (毕老师)ly; //将代表毕老师对象的父类引用ly强制转换成了毕老师类型。向下转型 /** * 注:如果父类可以创建对象,如:Animal a = new Animal(); * 此时,就不能向下转型了,Cat c = (Cat)a; 这样的代码就变得不容许,编译时会报错 * 所以千万不能出现这样的操作,就是将父类对象转成子类类型 * 我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。多态至始至终都是子类对象在做着变化 */ ``` ## 多态的特点 1. 非静态成员函数的特点:成员函数在多态调用时,编译看左边,运行看右边: * 在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败 * 在运行时期:参阅对象所属的类中是否有调用的方法。这就是说,如果父类中有一个非抽象的方法,而子类继承后又将其复写了,在多态运行时,父类的引用调用这个同名函数时,被运行的将是父类中的方法 2. 多态中成员变量的特点:无论编译和运行,都参考左边(引用变量所属的类) 3. 多态中静态成员函数的特点 无论编译和运行,都参考左边。也就是父类引用在调用静态同名函数时,被调用的是父类中的静态函数 这是因为,当类一被加载,静态函数就随类绑定在了内存中。此时,不需要创建对象,就可以使用类名直接调用。同时,父类中的静态成员函数一般是不被复写的 > 上述的2、3在实际开发中一般不会出现,只需了解运行结果即可 ## 静态绑定和动态绑定 成员方法在进内存时,进入的是方法区,方法区分成两部分,一个是静态方法区,一个是非静态方法区 静态方法区中的方法的所属是类名,即静态方法一进内存就和类名绑定在一起,是为静态绑定,在调用时使用类名调用 非静态方法区中的方法的所属是this或者super,即调用该方法的对象,是不固定的,是为动态绑定 这也就解释了上面的静态方法为什么无论编译运行都看左边 ## 多态的应用 1. 定义好工具类,即将共同行为封装在一个类中 2. 对类型进行抽取,---->多态的产生 3. 操作同一父类型,对其中的子类型均可操作 # 内部类 ## 概述 将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类) 当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物在使用外部事物的内容 如定义一个描述人的类,而手、心脏等都属于人,然它们又有自己的功能描述,这时可以在人这个描述类中,定义一个描述心脏的类,也就是内部类 编译时,如果代码中有内部类,生成的class文件中会含有这样的文件:Test$1.class 编译器将会把内部类翻译成用$(美元符号)分隔外部类名和内部类名的常规类文件。这是内部类的一种编译现象 ## 内部类的访问规则 1. 内部类可以直接访问外部类中的成员,包括私有 之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式:外部类名.this 2. 外部类要访问内部类,必须建立内部类对象 ## 访问格式 1. 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。可以直接建立内部类对象 格式:外部类名.内部类名 变量名 =外部类对象.内部类对象; 如:Outer.Inner in =new Outer().new Inner(); 2. 当内部类在外部类中的成员位置上时,可以被成员修饰符private修饰: 将内部类在外部类中进行封装 3. 当内部类在外部类中的成员位置上时,可以被成员修饰符static修饰: 内部类就局部static的特性。但是当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限 4. 在外部其他类中,直接访问static内部类的非静态成员,格式为: new 外部类名.内部类名().方法名() 如:new Outer.Inner().function(); 5. 在外部其他类中,直接访问static内部类的静态成员格式为:外部类名.内部类名.方法名(); 如:Outer.Inner.function(); 注意: * 当内部类中定义了静态成员时,该内部类必须是static的 * 当外部类中的静态方法访问内部类时,内部类必须是static的 * 在实际应用中,内部类通常被定义为private,而很少定义为public ## 内部类定义在局部访问格式 内部类定义在外部类中的某个方法中,创建了这个类型的对象时,且仅使用了一次,那么可在这个方法中定义局部类 1. 不可以被成员修饰符修饰。如public、private、static等修饰符修饰。它的作用域被限定在了声明这个局部类的代码块中 2. 可以直接访问外部类中的成员,因为还持有外部类中的引用 3. 内部类不可以访问它所在的局部中非最终变量。只能访问被final修饰的局部变量。 ```java class Outer { int x = 3; void method(final int a) { class Inner { void function() { System.out.println(); } } new Inner().function(); } } class InnerClassDemo { public static void main(String[] args) { Outer out = new Outer(); out.method(5); out.method(6); } } ``` > 为什么上面的代码中打印的值为什么会改变呢? 被final修饰的变量的值是不会被改变的。这里类调用方法使用完后,这时这个被final修饰的变量已经从栈内存中消失了,类再次调用这个方法时,已经是另一变量,所以可以重新被传值 ## 匿名内部类 1. 匿名内部类其实就是内部类的简写格式 2. 定义匿名内部类的前提: 内部类必须是继承一个类或者实现接口 特殊情况:因为所以的类都有一个父类Object,所以在定义时也可以用Object 3. 匿名内部类的格式:new 父类或者接口(){定义子类的内容} 这就定义了该父类或者接口的匿名子类对象,这就是匿名内部类的本质,可以理解为带内容的对象。可以用过该对象一次性的调用成员方法或成员变量 4. 匿名内部类中定义的方法最好不要超过3个 ```java abstract class AbsDemo { abstract void show(); } class Outer { int x = 3; /* class Inner extends AbsDemo { void show() { System.out.println("show:"+x); } } */ public void function() { //new Inner().show(); //上面注释掉的就是内部类的正常写法 //匿名内部类将上面的代码合为一步来做 //建立父类对象,但不以;结尾,而是用{} //{}中复写父类的方法,就是建立一个匿名的内部类 //而new了之后则是创建了AbsDemo的子类对象 //.show();是用对象调用方法 new AbsDemo() { void show() { System.out.println("show:"+x); } }.show(); } } class InnerClassDemo3 { public static void main(String[] args) { new Outer().function(); } } ``` ### 匿名内部类的利与弊 * 好处:简化书写 * 弊端: 1. 只能使用一次,因为没有名字 2. 如果进行多次使用需要给该类起名字,但是该类都没名字,想起名字只能用父类引用指向该对象(多态),但不能直接调用自己的特有方法 3. 不能做强转动作 4. 如果继承的父类或接口中有很多方法时,使用匿名内部类阅读性会非常差,且调用会很麻烦。所以匿名内部类中定义的方法有一般不超过3个(不包含三个) ### 匿名内部类的应用场景 当某个函数使用了某接口类型的参数,查看该接口时发现接口不超过三个抽象方法。可以使用匿名内部类作为实际参数传递 ```java interface Inter { void method(); } class InnerClassTest { public static void main(String[] args) { show(new Inter(){ public void method()//因为接口的method方法是public类型的 { //... } }); } public static void show(Inter in) { in.method(); } } ``` -separator-