1. 面向对象
一切事物都可以被称为对象。
2. 什么是面向对象
面向对象就是对面向过程的封装,面向对象是一种思想,而不是一种技术。java底层封装的很多东西就是c语言的东西。
2.1 类与对象
2.1.1 什么是类:
类是具有相同属性和行为的同一种事务的描述,是一个抽象的概念。
2.1.2 在代码中的体现:
用class修饰的文件就是类:public class 类名称 { }
在Java中类文件是最小的编程单位
2.1.3 属性和行为:
- 属性:我们使用变量来描述类的属性
- 行为:使用方法来体现行为
2.1.4 对象:
对象是类的现实存在的事务的体现,通过new关键字来创建对象,创建对象就是在堆中开辟一段属于类对象独有的空间,类可以实例化多个对象,每一个对象的地址值是不一样的
通过.(的的意思)来进行对象的属性的调用 并且可以对属性进行赋值 或者获取属性的值
2.2 定义Person类
public class Person {
//类属性
//名字
String name ;
//年龄
int age;
//身高
double height;
//体重
double weight;
//类的行为
public void say() {
System.out.println(" say hello!");
}
}
2.2.1 创建Person对象
public class PersonDemo {
public static void main(String[] args) {
//对类的实例化
//对类类型的对象的创建
Person zhangsan = new Person();
//com.xdkj.javase.oop.Person@15db9742
System.out.println(zhangsan);
//new 在堆内存开辟空间 属性是变量 有数据类型 保存的是默认值
System.out.println(zhangsan.name);//null
System.out.println(zhangsan.age);//0
System.out.println(zhangsan.height);//0.0
System.out.println(zhangsan.weight);//0.0
//对象调用方法
zhangsan.say();
//类可以实例化多个对象
Person lisi = new Person();
//com.xdkj.javase.oop.Person@6d06d69c
System.out.println(lisi);
//属性进行赋值
lisi.name = "李四";
lisi.age = 28;
lisi.height = 175.0;
lisi.weight = 150.0;
System.out.println(lisi.name);
System.out.println(lisi.age);
System.out.println(lisi.height);
System.out.println(lisi.weight);
//对象.方法名();
lisi.say();
}
}
2.2.2 类和对象的关系:
所有的事物都可以看作一个对象,是对象就具有一定的属性和功能,这些对象式可以建立起联系的,而且这些对象是由类构造出来的。类是具有属性和方法的一组对象的集合,对象是实际存在的该类事物的个体(类是对象的模板,对象是类的实例)
3. Java中的面向对象
3.1 封装
- 项目的封装:对一个项目的业务共能的封装
- 包的封装:相同描述或者类型的代码的封装
- 类的封装:把一个类的属性和行为进行封装
- 类的属性我们要进行私有化,对外提供公开的访问方式 get set方法
a. 代码功能的完整性
b. 减少代码的耦合 (高内聚 低耦合) - 对行为功能的封装 方法
public class Person {
//进行属性的私有化
private String name;
private int age;
private double height;
//提供属性的公开的访问方式
//获取属性的值
public String getName() {
return this.name;
}
//设置属性的值
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return this.height;
}
public void setHeight(double height) {
this.height = height;
}
public void say() {
System.out.println("say Hello World");
}
}
public class PersonDemo {
public static void main(String[] args) {
//类的实例化
Person p = new Person();
p.setName("李四");
System.out.println(p.getName());
p.say();
Person person = new Person();
person.setName("王五");
person.setAge(88);
person.setHeight(180.0);
System.out.println(person.getName() + " " +person.getAge()+ " "+ person.getHeight());
person.say();
}
}
3.1.1 构造器
构造器(构造方法)就是我们常见类的实例化对象时候的模板
在Java中每一个类都有一个默认的无参构造器,隐式的构造器。看不见,但是是默认提供的。
- 构造器不是方法,没有修饰符和返回值类型
- 构造器的名称必须和类名一致(都是大驼峰命名法)
- 构造器可以重载
- 如果使用了带参的构造器,无参的构造器必须手动写出。不然带参数的构造器会覆盖无参数的构造器
- 虽然无参数的构造器是默认的并且是隐式的 ,但是我们要去在编程中必须手动写出无参数的构造器
- 在使用带参数的构造器的时候,实际传入的参数的数据类型和构造器中的数据类型必须一致。
public class Dog {
private String dogName;
private int dogAge;
private String dogColor;
public Dog() {
}
//构造器的重载
public Dog(String dogName) {
this.dogName = dogName;
}
public Dog(String dogName, String dogColor) {
this.dogName = dogName;
this.dogColor = dogColor;
}
public Dog(String dogName, int dogAge, String dogColor) {
this.dogName = dogName;
this.dogAge = dogAge;
this.dogColor = dogColor;
}
3.1.2 this关键字
当前类的实例化对象引用,谁用就代表谁(被static修饰的方法中不能使用this关键字,因为被static修饰的方法在编译时就被加载了,但是对象还没有产生,所以找不到this)
作用:
- 不同的对象调用同一个方法,访问的是不同对象内部的属性。
- 用来区分局部变量和成员变量。
- 在构造方法的第一句调用本类的其他的构造方法。
3.1.3 JavaBean
javaBean我们也叫作一个普通的java对象(Plain Old Java Object)。对对象所在的类有这么几个要求:
1. 必须有私有的成员变量(成员属性)
2. 必须给私有的成员变量提供公开的访问方式 get set 方法
3. 必须有无参数的构造器。带参数的构造器看实际的需要 ,需要的时候在添加。
3.1.4 final关键字
- final修饰的变量是常量,不能更改
- 修饰局部变量:
* 对于基本数据类型来说:数值不可变
* 对于引用数据类型来说:地址值不可变,所指的对象内容也不可变 - 修饰成员变量:
* 由于有默认值,所以用了final后必须手动赋值, 不在给默认值了,也就是必须被初始化。
* 对于final的成员变量,要么直接赋值,要么通过构造器赋值,二选一。
* 必须保证类中所有重载的构造方法,都最终会对final修饰的成员变量赋值,不能保证则直接赋值,使用了final所以就不能写setXxx()方法了。
- 修饰局部变量:
- 修饰方法:不能被重写
- 修饰类:不能被继承,不能有子类,但是可以有父类
- 对于类和成员方法来说,abstract和final不可以同时用,因为矛盾
3.1.5 匿名对象
匿名对象就是没有名字的对象,也就是不用变量去接收他,new一次就开辟一次空间。
匿名对象只在调用的当前行有效,普通对象不会在方法结束后立即被回收,但是匿名对象在使用完后就被释放了,一次性的。
匿名对象更多是用在方法的参数中,调用方法时传入匿名对象作为参数。
3.1.6 static关键字
static关键字就是静态的意思,使用static修饰的成员变量,常量,方法,代码分别成为静态变量,静态常量,静态方法,静态代码块,他们统称静态成员。静态成员归整个类所有,不依赖特定的实例,被类的所有实例所共享的,只要类被加载就放置到了内存的静态区中
类的成员变量分为两种(成员变量 没有赋值,则在内存中保存的是默认值)
实例变量:没有被static修饰的变量
- 每创建一个实例,JVM就会为实例变量分配一次内存,可以在内存中有多个备份,互不影响。隶属于对象,在类的内部,本类中的静态方法或其他类,需要通过被类的实例对象对象进行访问
静态变量:
- 加载类的过程中完成静态变量的内存分配,只分配一次内存,并且在内存中只有一份,可以被所有的对象之间共享,在本类中,任何方法都可以直接访问静态变量,其他类中需要通过**类名.**来访问
静态方法和和静态变量一样,可以不通过本类的实例对象而通过“类名.静态方法名”就可以调用。静态方法不能访问本类的实例变量和实例方法。因为静态资源在编译时(对象未实例化时)就被加载了,而非静态资源在运行时才被加载,所以静态方法只能调用静态变量,不能调用非静态资源!
- 静态的属性:代表对象共有的属性,静态的属性在声明的时候要给初始值,静态的属性名要求所有的字母大写,多个单词用下划线隔开
3.1.7 访问修饰符
在一个源码文件中只能有一个public class类。
public 公开的: 可以修饰类,属性,方法,在哪都可以访问。
protected 受保护的:不能修饰类,修饰的属性和方法不能在其他包中访问。
default 默认的(不添加修饰符):可以修饰类,属性,方法,不能在其他包中和不同包中的子类中访问。
private 私有的:只能在本类中访问。
3.1.8 代码块
代码块分为局部代码块,构造块(全局代码块),静态代码块
局部代码块:在方法内部的代码块,实际作用就是代码隔离
全局代码块:在类的对象被调用的时候执行,全局的代码块最先被执行
静态代码块:主要用于初始化类,为类的静态属性初始化,有以下几个特点:
- 静态代码块不能存在于任何方法内
- 不能直接访问实例变量和实例方法
- JVM在类的第一次加载时就会执行静态代码块,所以静态代码块优先于主方法执行也优先于全局代码块。
- 无论创建多少个对象,静态代码块只会执行一次
class Code{
{ //构造块
System.out.println("Code的构造块");
}
static{ //静态代码块
System.out.println("Code的静态代码块");
}
public Code(){ //构造方法
System.out.println("Code的构造方法");
}
}
public class Codeblock03{
{ //构造块
System.out.println("Codeblock03的构造块");
}
static{ //静态代码块
System.out.println("Codeblock03的静态代码块");
}
public Codeblock03(){ //构造方法
System.out.println("Codeblock03的构造方法");
}
public static void main(String[] args) {
System.out.println("Codeblock03的主方法");
new Code(); //创建Code对象
new Code(); //创建Code对象
new Codeblock03(); //创建Codeblock03对象
new Codeblock03(); //创建Codeblock03对象
}
}
程序中的执行顺序:
public中静态代码块 -->主方法–>构造块–>构造方法
3.1.9 变量的作用域
- 全局变量
- 局部变量
- 成员变量和成员方法
- 静态成员方法和静态成员属性
/**
* 变量:
* a. 全局变量
* 声明在类的属性的位置 全局变量可以被类中的所有方法使用
* 全局变量的生命周期是什么?
* 当声明的全局变量的类不在被引用的时候我们就确定类不在被使用了,但是GC也不会立即回收
*
* b. 局部变量
* 局部变量的生命周期就是方法执行完成以后从内存中释放。
* c. 局部变量和全局变量同名的时候 使用就近原则
*
* d. .java文件中的静态内容在编译时候就放置到静态区内存中 .class文件中的静态的常量在类被调用或者
* 导入的时候放置到静态的内存区中
* */
public class VariableDemo {
//全局变量
static int num = 100;
public static void main(String[] args) {
int num = 200;
System.out.println(num);
method();
VariableDemo vd = new VariableDemo();
System.out.println(vd);
//空
vd = null;
//就近原则
System.out.println(num);
System.out.println(Calendar.DATE);
}
public static void method() {
System.out.println(num);
}
}
最简单的加快GC回收的方法 :给对象赋null值,解除该变量与内存的引用,让对象变为匿名对象。
3.2 继承
在java中我们,如果我们在一个类中定义了属性和行为 那么他的子类就直接继承就可以了,不要在自己去定义了。
extends 关键字就是继承的意思:
- 子类继承自父类 那么子类就具有类父类的属性和行为
- 子类继承父类 那么子类也可以对自己的属性和行为进行扩增
- java中只能单继承 一个类只能继承一个类。
- Java我们可以实现多层继承
3.2.1 重写(Override)
- 必须要有继承关系
- 子类对父类的方法进行重写,方法的声明一样但是方法的实现不一样
重写和重载的区别
- Overload:方法重载是在同一个类中,实现同一个方法名不同的功能,与方法的参数列表(参数顺序,参数个数,参数类型有关)与返回值无关,在1.7之后不用写@Overload注解
- Override:重写的前提是必须有继承关系,子类继承父类,在子类中继承来自父类的属性和行为,方法名相同,参数列表相同,重写的方法的权限修饰符必须大于父类的权限修饰符。
- 返回值为基本数据类型时:重写方法的返回类型必须和父类方法的返回类型相同。
- 返回值是引用数据类型时:子类返回值范围必须小于等于父类的返回值范围。
3.2.2 super关键字
super代表父类引用
super必须在构造器的第一行
super是隐式的。默认在构造器的第一行隐藏的,手动写会覆盖默认的
super()代表父类的无参构造的调用
3.2.3 super和this的区别
this代表当前类的实例化对象的引用,谁用就代表谁
super代表父类引用
- 在没有继承关系的无参构造器的第一行,super()代表Object超类的无参构造器
- 在继承关系的无参构造器或者带参数的构造器的第一行,super()代表父类无参数构造器
- super永远都在第一行,一般super是隐式的,可以手动写出
3.2.4 Object类
类Object是类层次结构的根类。每个类都是用Object作为超类。所有对象(包括数组)都实现这个类的方法
Object只有一个无参构造器,默认被所有的类继承
- toString():输出对象本质就是在使用对象调用toString()
- getClass().getName()+’@’+Integer.toHexString(hashCode())
- Object中建议所有子类重写toString方法是为了方便查看对象的属性值。
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", height=" + height + ", address=" + address + "]";
}
3.2.5 equals和==的区别(面试题)
可以查看源码自己写出equals方法和hashCode方法
- ==比较的是对象在内存中的地址值,不适合做对象间细节的比较,所谓的细节就是对象之间属性的比较
- 追踪源码发现Object类中的equals方法底层也是使用==来比较对象堆内存的地址值,所以就必须重写equals方法来比较对象之间的属性是否相等
- equals比较的对象必须是同一种类 类型的对象
- 所以需要使用instanceof关键字判断传入的对象是否是类类型的实例化对象或者类类型的子类的实例化对象
- 当此方法重写时,通常就必须重写hashCode方法,以维护hashCode方法的常规协定,该协定声明相等对象必须具有相同的哈希码
- hashCode方法的常规协定:如果根据equals(Object)方法,两个对象中的每个对象调用hashCode方法都必须生成一个相同的整数结果
乘以31的原因:对计算机来说,运算更快
@Override
public int hashCode() {
final int prime = 31;//对计算机来说,运算更快
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
3.3多态
3.3.1 什么是多态
多态:同一种事务存在不同的形态(针对于对象,而不是类)
父类(接口)引用指向子类(接口实现类)的实例化对象
3.3.2 多态的使用:
- 必须有继承关系
- 父类(接口)引用指向子类(接口实现类)的实例化对象
- 必须有方法的重写
格式:
父类名称 对象名 = new 子类名称();接口名称 变量名=new 实现类名称();
成员变量重名的访问特点:(成员变量不能被重写)
- 编译看左边,运行看左边
- 直接通过对象名访问成员变量:看等号左边是谁,优先用谁,没有则向上找。(不向下找)
- 间接通过成员方法访问:看该方法属于谁,优先用谁,没有向上找。
成员方法的访问特点
- 编译看左边,运行看右边
- new谁就优先用谁,没有向上找,子类如果重写了方法,优先用子类。
3.3.3 使用多态的好处:
3.3.4 对象的向上/向下转型
- 向上转型:多态写法,一定是安全的(从范围小的转向范围大的)
含义:把一个子类对象当作父类看待,一旦向上转型成功,则无法使用子类特有方法 - 向下转型(还原):
将父类对象还原成本来的子类对象
格式:子类名称 对象名=(子类名称)父类对象
instanceof关键字:用来判断本来的类(向下转型使用)
格式:对象 instanceof 类名称
返回一个布尔值
3.4 抽象类和抽象方法
- 抽象:abstract
- 抽象类就是用abstract修饰的类
- 抽象方法就是只有方法的声明,没有方法体(方法的实现)
- 有抽象方法的类一定是抽象类,抽象类不一定有抽象方法,可以有普通方法
- 抽象类中的抽象方法在子类中必须被重写,普通方法可以选择性的重写。除非子类也是一个抽象类
- 抽象类不能实例化对象(因为创建了抽象类的对象的话,调用方法没有具体的方法体,没有意义)
- 抽象类可以有构造器,是要提供子类实例化对象初始化父类成员使用的,自己不能实例化对象
- 抽象类有属性可以是变量也可以是常量
- 抽象类可以有静态的成员属性和行为,通过类名调用(通过静态的访问方式访问)
public abstract class Car {
//私有的成员属性 变量
private int age;
//成员属性 常量
final int count = 2000;
//静态常量
static final String FACTORY = "华晨";
public Car() {
super();
}
public Car(int age) {
super();
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void run() ;
public void eat() {}
}
public class Bwm extends Car {
public Bwm() {
super();
}
public Bwm(int age) {
super(age);
}
@Override
public void run() {
System.out.println("宝马车行驶!!!");
}
@Override
public void eat() {
System.out.println("bwm eat oil!!!");
}
}
3.5 接口
3.5.1什么是接口
接口就是一种公共规范标准,引用类型
格式:public interface 接口名称 { 抽象方法(); }
3.5.2 在JDK9中接口中的内容可以有:
1. 成员变量其实就是常量,格式:
【public】【static】【final】数据类型 变量名称 = 数据值;
注意:
- 常量必须进行赋值,而且一旦赋值不能改变
- 常量名称必须大写,多个单词用下划线隔开
2. 接口中最重要的是抽象方法,格式:
【public】【abstract】返回值类型 方法名称(参数列表);
- 实现类必须重写接口中的所有抽象方法,除非实现类是抽象类
3. 从JDK 8开始接口允许定义默认方法,格式:(解决接口升级问题)
【public】default 返回值类型 方法名称(参数列表){方法体};
- 注意:默认方法也可以被重写
4. 从JDK 8开始接口允许定义静态方法,格式:
【public】static 返回值类型 方法名称(参数列表){方法体};
- 应该通过接口名称进行调用,不能通过实现类对象调用静态方法
5. 从JDK 9开始接口允许定义私有方法,格式:
- 普通私有方法:private 返回值类型 方法名称(参数列表){方法体} (解决多个默认方法之间的重复代码问题)
- 静态私有方法:private static 返回值类型 方法名称(参数列表){方法体} (解决多个静态方法之间的重复代码问题)
- 注意:私有方法只能接口自己才能使用,不能被实现类或别人使用
3.5.3 接口的注意事项
- 接口没有静态代码块
- 接口不能有构造器
- 一个类只能继承一个父类,但是可以同时实现多个接口
- 接口与接口是多实现的,类与接口是多继承的
- 如果并没有全部实现接口的所有抽象方法,那么这个实现类就必须是抽象类
- 如果实现类所实现的多个接口中存在重复的抽象方法,只需要覆盖重写一次
- 如果实现类所实现的接口中,存在重复默认方法,那么实现类一定要对冲突的默认方法重写
- 如果一个类的直接父类的方法和接口中的默认方法产生冲突,优先调用父类中的方法,即继承优先于接口
- 如果多个父接口中存在重复的抽象方法,没关系
- 如果多个父接口中存在重复的默认方法,那么子接口必须对默认方法覆盖重写
- 接口中的变量是常量
package character2;
public interface AD {
public abstract void attrack();
public default void method(){
System.out.println("这是物理攻击英雄");
}
}
System.out.println("============================================");
package character2;
public interface AP {
public abstract void attrack();
public default void method(){
System.out.println("这是魔法攻击英雄");
}
}
System.out.println("============================================");
package character2;
public interface ADAPHero extends AD, AP {
//重复的抽象方法不需要覆盖重写
//重复的默认方法需要在子接口覆盖重写
@Override
default void method() {
System.out.println("这是进行物理攻击和魔法攻击的英雄");
}
}
System.out.println("============================================");
package character2;
public class Hero implements ADAPHero {
//重复的抽象方法在实现类需要覆盖重写
@Override
public void attrack() {
System.out.println("抽象方法,物理魔法攻击");
}
public static void main(String[] args) {
Hero hero = new Hero();
hero.attrack();
hero.method();
}
}
3.6 内部类
- 成员内部类
- 局部内部类
- 静态内部类
- 匿名内部类(lambda表达式)
3.6.1 成员内部类
/**
* 内部类: 定义在一个类内部的类称之为内部类
* 1. 成员内部类 写在成员属性位置的内部类
* public protected default private static final
* 2. 局部内部类
* 3. 匿名内部类
*
* */
public class Outer {
private String name ;
int count = 20;
//内部类
class Inner{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
成员内部类访问外部成员
/**
* 成员内部类可以访问外部类的成员属性
*
* */
public class Outer5 {
int count = 100;
public class Inner6{
public void method() {
//外部类的对象
Outer5.this.count = 400;
System.out.println(count);
//方法只有被调用才能被执行
//Outer5.this.hello();
hello();
}
}
public void hello() {
System.out.println("Hello Outer5");
}
public static void main(String[] args) {
Outer5.Inner6 inner6 = new Outer5().new Inner6();
inner6.method();
}
}
3.6.2 局部内部类
public class Outer4 {
int num = 100;
public void method() {
System.out.println(num);
//局部内部类 几乎不用
class Inner5{
public void hello() {
num = 200;
System.out.println("Hello World "+ num);
}
}
new Inner5().hello();
}
public static void main(String[] args) {
Outer4 outer4 = new Outer4();
outer4.method();
}
}
3.6.3 静态内部类
public class Outer2 {
//静态内部类
static class Inner2{
public void hello() {
System.out.println("Hello World");
}
}
public static void main(String[] args) {
//静态内部类的访问方式
Outer2.Inner2 inner2 = new Outer2.Inner2();
System.out.println(inner2);
inner2.hello();
}
}
静态内部类访问外部类的成员:
/**
* 静态内部类只能访问外部类的静态成员属性和静态的成员方法
* 静态的东西属于类 不属于类的对象
*
* */
public class Outer6 {
static int age = 100;
static class Inner7{
int count = 200;
public void method() {
//谁在调用age
System.out.println("Inner7 "+ Outer6.age);
System.out.println("Inner7 "+ age);
//谁在调用hello
hello();
}
public int count() {
return count;
}
}
public static void hello() {
System.out.println(Outer6.class);
System.out.println("Hello Outer5");
System.out.println(new Inner7().count());
}
public static void main(String[] args) {
//静态内部类实例化对象
Outer6.Inner7 inner7 = new Outer6.Inner7();
inner7.method();
}
}
3.6.4 final修饰的内部类
public class Outer3 {
//final 修饰内部类
final class Inner3{
public void hello() {
System.out.println("Hello Inner3");
}
}
//final修饰的内部类不能被继承
/*
* class Inner4 extends Inner3{
*
* }
*/
public static void main(String[] args) {
Outer3.Inner3 inner3 = new Outer3().new Inner3();
}
}
3.7 匿名内部类
- 如果接口的实现类(或者是父类的子类)只需要使用一次,那么这种情况可以省略该类的定义改为匿名内部类
- 格式:接口名称 对象名 =new 接口名称(){ 重写所有接口中的抽象方法};
- 解析:
- new代表创建对象的动作
- 接口名称就是匿名内部类需要实现的那个接口
- {。。。}里面才是匿名内部类的内容
- 注意:
- 匿名内部类在创建对象时,只能使用一次,唯一一次,如果希望多次创建对象,而且内容一样的话,那么必须使用单独定义的实现类
- 匿名对象,在调用方法的时候,只能调用一次,如果希望是一个对象,调用多次方法,则必须给对象起名字,直接new的就是匿名对象
- 匿名内部类是省略了(实现类/子类名称)但是匿名对象是省略了对象名称
- 匿名对象和匿名内部类不是一回事!!!
因为匿名对象无法调用第二次方法,所以需要在创建一个匿名内部类的匿名对象才可以调用第二次,直接在 } ;后面调用
3.7.1 匿名内部类的使用
public interface Song {
void songEnglish();
}
public class People {
public void song(Song song) {
song.songEnglish();
}
}
import java.util.Comparator;
import java.util.TreeSet;
public class PeopleDemo {
public static void main(String[] args) {
People people = new People();
//传入接口的实现类
/*
* people.song(new Song() {
*
* @Override public void songEnglish() { System.out.println("唱的是嘻哈英文歌曲!"); } });
*/
Song song = new Song() {
@Override
public void songEnglish() {
System.out.println("唱的是嘻哈英文歌曲!");
}
};
people.song(song);
people.song(song);
people.song(song);
//TreeSet构造器 匿名内部类的使用
new TreeSet(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
Runnable rable = new Runnable() {
@Override
public void run() {
System.out.println("Hello ------");
}
};
//lambda表达式 函数是编程
Runnable runnable = ()->{
System.out.println("run method");
};
runnable.run();
}
}
3.7.2 匿名内部类分析
- 匿名内部类没有构造器,但是可以实例化对象
- 匿名内部类不能定义静态的属性
- 匿名内部类可以定义常量
- 匿名内部类可以扩增方法,但是调用的时候:直接调用会失去接口重写的方法,多态调用会失去扩增方法
- 一般在匿名内部类中不要扩增方法,不然就失去了匿名内部类原生的作用
- 匿名内部类不能创建静态资源
- 外部类的方法中的局部匿名内部类可以访问外部类的成员属性和方法··