引言1、先会用,在用的过程中逐渐加深对面向对象的理解。
2、利用不断地练习,形成肌肉记忆(代码格式)和意识记忆(要啥给啥)。(开发中重要的思想而不是语法)
面向对象的定义面向过程编程(POP --> Process-Oriented Programming)代表:C语言
面向对象编程(OOP --> Object-Oriented Programming)代表:C++、Java
面向切面编程(AOP --> Aspect-Oriented Programming)代表:Java
面向对象的思想是把一个项目、一件事情分成更小的项目,或者说分成一个个更小的部分,每一部分负责什么方面的功能,最后再由这些部分组合而成为一个整体。
找合适的人做合适的事
生活中的面向对象和面向过程举例:想吃烤羊排
面向对象思想面向对象:找个店铺吃
1、去羊排店
2、点一斤羊排
3、等25分钟
4、吃
5、付款走人
面向过程:自己做着吃
1、自己做羊排
1.准备材料(羊、辅料、调料)
2.备料
3.烤箱预热,开烤
4.15分钟翻面一次
5.取出
2、吃
3、洗锅刷碗,打扫卫生
1、面向对象是基于面向过程的编程思想 2、万物皆对象 3、对象具有唯一性 4、任何对象都具有一定的特征和行为;特征是事物的基本描述,行为是事物的功能 5、类是一组相关的属性和方法的集合,是一个抽象的概念 6、对象是类的具体存在 7、在一组相同或相似的对象中,抽取出共性的特征和行为,保留所关注的部分就是类的抽取 8、类是模板、图纸,通过类创造的对象就是实体面向对象的优点
- 直观,高效,与人类的思维习惯一致
- 信息隐藏,提高了程序的可维护性和安全性
- 提高了程序的可重用性
类和对象 生活中的类和对象面向过程:亲力亲为,自力更生,所有过程都要经历(诸葛亮是怎么死的,累死的)
面向对象:找合适的人做合适的事,直观高效(烤羊排)
这些对象有什么特点?
Java 中的类和对象 类人类:
马云爸爸,麻花藤
都是特立独行的个体,我们一提起这个个体脑海中就能浮现出他的形象,
隔壁老王、女朋友
这些都不是对象,因为每个人都会有自己的隔壁老王,都有自己的女朋友,你脑海中的隔壁老王和你同桌脑海中的隔壁老王是同一个人吗?除非你们是同一个邻居还姓王那就不说了。但你们的女朋友总不会也是同一个吧?所以说,女朋友,隔壁老王都不是对象,你的隔壁老王,你的女朋友才是个对象!!!
狗类:
史努比,忠犬八公,高飞,斯派克,小白,王可可
这些都是独一无二的
单身狗、旺财、金毛、哈士奇
这些全都是类
类是对某一类事物的描述,是抽象的、概念上的定义。类是模板,包含了一类事物所共有的特征(属性)和行为(方法)
以人类为例:
对象属性描述:学号、姓名、性别、年龄、身高、体重、地址、电话、微信、QQ…
行为描述:吃饭、睡觉、上班、学习、娱乐…
对象是类的具体体现(属性和方法),是具体的、独一无二的个体。
以班长为例:
类和对象的关系属性描述:班长的学号、班长的姓名、班长的身高…
行为描述:班长吃饭、班长睡觉、班长学习…
类是对象的抽象,对象是类的具体实现
类的定义类是一种自定义的数据类型
格式: class 类名 { 成员变量;// Field 成员方法;// Method } class: 定义类的关键字 类名: 大驼峰命名,首字母大写,见名知意 类名就是一种数据类型(自定义引用的数据类型),就是模板的名字 成员变量(属性/特征描述): 定义在类中,方法外的变量,用来描述类的特征 成员方法(行为描述): 定义在类中,用来描述类的功能
class Student { // 成员变量【Field】 String name; int age; char sex; // 成员方法【Method】 public void eat() { System.out.println("吃"); } public void sleep() { System.out.println("睡"); } public void play() { System.out.println("玩"); } }对象的创建
参考Scanner的创建,依葫芦画瓢
Scanner sc = new Scanner(System.in);
// 格式: 类名 对象名 = new 类名([参数...]);
Person person = new Person();对象的使用
参考数组和 Scanner 的使用,依葫芦画瓢
int[] array = new int[10]; array.length // 注意:这里没有 (),说明这不是方法,那它是什么?我们用它来表示数组的长度,数组的长度是数组的一个属性 Scanner sc = new Scanner(System.in); sc.nextInt();
格式: 使用成员变量: 对象名.成员变量 使用成员方法: 对象名.成员方法()
// 通过Person类对象person *** 作name、sex、age属性 // 进行赋值 *** 作 person.name = "张三"; person.age = 25; person.sex = '男'; // 通过person进行取值 *** 作 System.out.println(person.name); System.out.println(person.age); System.out.println(person.sex); person.eat(); person.sleep(); person.play();
. 的含义是 “的”
person.name:person的name
person.age:person的年龄
person.sex:person的性别
对象内存分析图扩展:
如果直接打印对象名,会得到一个对象的【地址】,这个地址包含两部分:1、完整的包名;2、当前对象存储在堆区内存中的空间首地址
构造方法【鸡肋】 引言没听懂没关系,多敲代码,读着代码就懂了
生活中的类和对象
- 类是事物的统称,是一个抽象的概念
- 对象是事物的表现,是具体的,独一无二的
Java中的类和对象
- 类是对象的抽象,具有公共的特征和行为,对象是类的具体实现,具有唯一的特征和行为
基础数据类型与类和对象
- 类就是一种自定义的数据类型,由类名,属性和方法构成
- 对象就是一种数据类型的数据,由对象名,属性和方法构成
类和对象的定义和使用
- 格式和规范,一定要按照标准
注意
大写开头的都是类 小写开头的都是变量 带()的都是方法
定义构造方法很重要,但是比较鸡肋
构造方法(Constructor)的名称和类名相同,没有返回值类型。
作用类中的特殊方法,用于创建对象,在创建对象的时候会执行一些初始化 *** 作,如给成员属性赋初值
格式格式: 类名([参数...]){} Student(){}
注意:格式问题
构造方法的使用1、构造方法的方法名与类名完全相同
2、构造方法没有返回值类型
3、创建对象时,触发构造方法的调用,不可手动调用
4、如果没有声明构造方法,编译器默认生成无参构造方法
5、如果定义了有参构造方法,编译器就不会创建无参构造方法
【强制要求】
无论什么时候,都要加上一个无参构造方法!!!
// 自定义有参构造方法,并给name属性赋值 Dog(String n) { name = n; } // 自定义无参构造方法 Dog(){}构造方法的重载
public Dog(String n){ name = n; } public Dog(String n, int i){ name = n; age = i; }总结
扩展:反编译
构造方法是用来创建对象,在创建对象的过程中会进行初始化 *** 作(为对象赋值)
构造方法也是方法,除了没有返回值,其他的都跟方法一样
按照方法参数列表的类型、个数、顺序去匹配,如果没有找到对应的就会报错
- 无论什么时候,都一定要给一个无参构造方法
javap -c -l -private 类名.class扩展:对象的创建过程
1、类加载 2、内存中开辟对象空间 3、为各个属性赋予初始值 4、执行构造方法中的代码 5、将对象的地址赋值给变量this关键字 概述
this 代表所在类的对象引用,即当前对象
作用new 创建出来的对象
调用方法的对象
1、调用本类中的属性和方法(区别成员变量和局部变量)
2、调用本类中的其他构造方法:this()
格式: this([参数...]); 会根据参数列表调用对应的构造方法
public Rabbit(String color) { // 调用本来中的属性 this.color = color; } public Rabbit(String color, int age, double weight) { // 调用本类中的其他构造方法 this(color); this.age = age; this.weight = weight; }
【注意】
1、this()只能在构造方法中使用 2、this()只能在第一行 3、构造方法中不能同时出现两个this(),因为2 4、不能自己调用自己,不能相互调用规范化this()
class Son { String name; int age; float salary; public Son() { } public Son(String name) { // 调用Son(String name, int age, float salary) this(name, 0, 0.0F); } public Son(String name, int age) { // 调用Son(String name, int age, float salary) this(name, age, 0.0F); } public Son(String name, int age, float salary) { this.name = name; this.age = age; this.salary = salary; } }总结
访问(权限)修饰符 private(私有)关键字
- this表示的是当前对象
- this可以调用本类中的属性和方法,最常用于区分成员变量和局部变量
- this还可以调用本类中的构造方法,但是要注意有坑
1、可以修饰成员变量和成员方法 2、被private修饰的变量和方法仅本类中可用 3、被private修饰的变量需要提供get、set方法供类外调用使用 4、boolean类型的 get 方法比较特殊: public boolean isName(String name){ return name; }
public class Dog { private String name; int age; public String getName() { return this.name; } public void setName(String name) { this.name = name; } private void function() { System.out.println("method be execute!"); } public void executeFunction() { this.function(); } }Java中的访问修饰符
封装 面向对象三大特征1、一般我们最常用的就是private和public,建议任何情况下都使用访问修饰符对变量和方法进行限制
2、public权限最高,整个项目中都可以访问(同一个项目),private权限最小,只能在本类中使用
3、被private修饰的变量和方法可以通过提供公共的方法对其进行访问
概述封装
继承
多态
是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。核心是归纳总结
好处提高代码的复用度、安全性,不必关心具体细节,便于开发
JavaBean 规范化封装1. 要求Java中的所有实体类成员变量全部私有化,最少提供一个无参数构造方法,对应成员变量实现setter和getter方法 2. JavaBean规范,是为了后期开发汇总更好的代码适配度,提高代码运行的统一性,能够满足框架的使用 3. JavaBean规范只是一个规范,而且是作为一个基础规范, *** 作都是可以使用快捷键来完成的!!!
class Person { private String name; private int age; private char sex; private boolean alive; public Person() {} public String getName() { return this.name; } public int getAge() { return this.age; } public char getSex() { return this.sex; } public boolean isAlive() { return alive; } public void setAlive(boolean alive) { this.alive = alive; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setSex(char sex) { this.sex = sex; } }类与类之间的调用(多类合作)
汽车类
public class Car { private String color; private float speed; private int tyreTotal; // 将自定义的引擎类作为汽车类的成员变量 private Engine engine; public Car() { super(); } public Car(String color, float speed, int tyreTotal, Engine engine) { super(); this.color = color; this.speed = speed; this.tyreTotal = tyreTotal; this.engine = engine; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public float getSpeed() { return speed; } public void setSpeed(float speed) { this.speed = speed; } public int getTyreTotal() { return tyreTotal; } public void setTyreTotal(int tyreTotal) { this.tyreTotal = tyreTotal; } public Engine getEngine() { return engine; } public void setEngine(Engine engine) { this.engine = engine; } public void race() { if (4 == tyreTotal) { System.out.println("开着" + this.color + "颜色的保时捷以" + speed + "迈的速度飙车"); } else { System.out.println("轮胎出问题了,要找修理厂"); } } }
修理厂类
public class Factory { private String name; private String tel; private String address; public Factory() { super(); } public Factory(String name, String tel, String address) { super(); this.name = name; this.tel = tel; this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Car fix(Car car) { System.out.println("欢迎来到" + this.name + "修车厂,我们的电话是:" + this.getTel() + ",我们的地址是:" + this.address); if (car.getTyreTotal() != 4) { car.setTyreTotal(4); // 将轮胎改成4个 car.setSpeed(80.0F); // 速度降到80迈 System.out.println("修车ing~~~"); } // 拿到汽车的引擎 Engine engine = car.getEngine(); // 拿到引擎的名字和排量 String engineName = engine.getName(); float engineDisplacement = engine.getDisplacement(); if (null != engineName) { System.out.println("检查引擎没有问题,排量是:" + engineDisplacement + "T"); } System.out.println("修理完毕"); return car; } }
引擎类
public class Engine { // 型号 private String name; // 排量 private float displacement; public Engine() { super(); } public Engine(String name, float displacement) { super(); this.name = name; this.displacement = displacement; } public String getName() { return name; } public void setName(String name) { this.name = name; } public float getDisplacement() { return displacement; } public void setDisplacement(float displacement) { this.displacement = displacement; } }
测试类
public class TestCar { public static void main(String[] args) { // 这里有一个引擎 Engine engine = new Engine(); engine.setName("XX"); engine.setDisplacement(1.0F); // 买了一辆保时捷 Car car = new Car("红色", 300.0F, 4, engine); // 飙车 for (int i = 1; i <= 10; i++) { car.setSpeed(car.getSpeed() + 2); car.race(); } // 如果车的速度太快,达到了320迈,就会爆胎 if (car.getSpeed() >= 320.0) { // 有一个车轱辘爆胎了 car.setTyreTotal(3); } // 继续飙车 car.race(); // 找修理厂 Factory factory = new Factory("摇滚王子修车厂", "15844517927", "吉安"); // 修车,拿到修好的车 Car fixedCar = factory.fix(car); // 飙车 for (int i = 1; i <= 10; i++) { fixedCar.setSpeed(fixedCar.getSpeed() + 2); fixedCar.race(); } // 随便找个修车厂修车,此时我只关心修车,不在乎其他的内容 // new Factory().fix(car); } }匿名对象 概述
没有名字的对象,是对象的一种简化表示形式
特性一次性,每次使用都是一个新的对象
使用情景1、对象调用方法仅使用一次,然后等待销毁
2、作为实际参数传递
public class TestDog{ public static void main(String[] args) { // 对象调用方法仅使用一次 new Dog().sleep(); // 作为实际参数传递 useDog(new Dog()); } public static void useDog(Dog dog) { dog.sleep(); } } class Dog { String name; int age; public void sleep() { System.out.println("小狗睡觉....."); } }优点
提高开发效率,简化代码结构
继承 概念把多个类中相同的成员给提取出来定义到一个独立的类中。然后让这多个类和该独立的类产生一个关系,这多个类就具备了这些内容。这个关系叫继承。
关键字:extends 格式: class Son extends Father { } 一个孩子只能有一个父亲 一个父亲可以有多个孩子特点
1、Java为单继承,一个类只能有一个直接父类,但可以多级继承,属性和方法逐级叠加 2、构造方法只可服务于本类,不可继承,子类执行构造方法前会默认调用父类的无参构造方法。可以通过super()去访问父类的构造方法 3、private 修饰的属性和方法不能被继承extends
public class Demo { public static void main(String[] args) { // 创建一个父类对象 Father father = new Father(); // 父类调用父类的属性和方法 father.name = "父"; System.out.println(father.name); father.game(); System.out.println("-------------"); // 创建一歌子类对象 Son son = new Son(); // 子类调用子类的属性和方法 son.age = 16; System.out.println(son.age); son.study(); // 子类调用父类的属性和方法(public修饰) son.name = "子"; System.out.println(son.name); son.game(); // son.suffer = 10; // son.cook(); } } public class Father { public String name; private int suffer; public void game() { System.out.println("下棋"); } private void cook() { System.out.println("做饭"); } } public class Son extends Father{ public int age; public void study() { System.out.println("子类 -- 学习"); } }子类构造方法执行前默认先执行父类的无参构造方法
class Father { String name; public Father() { System.out.println("Father's Constrator be performed"); } } class Son extends Father { int age; public Son() { System.out.println("Son's Constrator be performed"); } } public class TestSon { public static void main(String[] args) { Son son = new Son(); } }
结果
Father's Constrator be performed Son's Constrator be performed
【注意】Son 的构造方法中编译器默认生成 super(); 用来调用父类的构造方法,目的是为了初始化父类字段,因为子类可能会用到
1、提高了代码的复用性 2、提高了代码的维护性 3、让类与类之间产生了一个关系,是多态的前提缺点
1、让类的耦合性增强。这样某个类的改变,就会影响到其他和该类相关的类 2、打破了封装性总结
方法重写【Override】 引言Java中只有单继承
子类可以继承父类的非私有属性和方法(非private修饰的)
执行子类的构造方法前会默认执行父类的无参构造方法
开发中父类的方法不一定适用于子类,因为父类方法不能更改,在子类中新增方法会造成代码的冗余,而且不符合逻辑
要求1、应用于继承和实现接口 2、方法的返回值类型,方法名,形参列表与父类一致 3、使用@Override注解来标识 4、重写方法的访问修饰符权限不能低于父类 private < 默认(什么都不写) < protected < public
public class Son extends Father{ public int age; @Override public void game() { System.out.println("玩红色警戒"); } public void study() { System.out.println("子类 -- 学习"); } } public class Father { public String name; private int suffer; public void game() { System.out.println("下棋"); } private void cook() { System.out.println("做饭"); } } public class Demo1 { public static void main(String[] args) { Son son = new Son(); son.game(); } }优点
既沿袭了父类的方法名,又实现了子类的扩展
总结super 关键字 概述1、方法的重写能够在不新增方法的情况下实现子类的扩展
2、方法重写要求方法声明格式和父类完全一致(访问修饰符不能小于父类)
3、@Override关键字用来开启格式检测,如果不一致就会报错
调用父类的属性和方法super指父类对象,用来区分父类和子类,用于调用父类的属性和方法
用法和this非常类似:this指当前对象,super指父类对象
public class Father { public int age = 60; public void play() { System.out.println("下象棋"); } } public class Son extends Father { public int age = 16; @Override public void play() { System.out.println("玩游戏"); } public void showAge() { int age = 20; System.out.println("局部变量:" + age); System.out.println("当前对象成员变量:" + this.age); System.out.println("父类对象成员变量:" + super.age); } public void callPlay() { // 调用当前对象的方法 this.play(); // 调用父类对象的方法 super.play(); } } public class Demo { public static void main(String[] args) { Son son = new Son(); son.showAge(); son.callPlay(); } }调用父类的构造方法
默认调用父类的无参构造,且必须在代码的第一行
class Father { private String name; public Father() { System.out.println("Father's Constrator be performed"); } public Father(String name) { System.out.println("Father's Constrator be performed with name"); } } class Son extends Father { private int age; public Son() { super(); System.out.println("Son's Constrator be performed"); } public Son(String name, int age) { super(name); this.age = age; System.out.println("Son's Constrator be performed with name and age"); } } public class TestSon { public static void main(String[] args) { Son son = new Son(); } }
【注意】super() 和this() 代码不能共存(都必须在首行),但是实际效果其实是可以的,如果不写 super() 也会自动调用
总结final 关键字 概述1、super指父类对象,对比this关键字,使用方法都一样
2、super() 和this() 代码不能共存(都必须在首行),但是实际效果其实是可以的,如果不写 super() 也会自动调用
3、父类的属性要交给父类的构造方法去 *** 作,没什么事就不要去使用 super() 来调用父类的构造方法了
final表示最终的,用来修饰变量,方法和类
1、final 修饰的局部变量只能被赋值一次 2、final 修饰的成员变量只能被赋值一次,并且必须在声明时就赋值 3、final 修饰的基本类型变量是一个常量(只能被赋值一次),引用类型变量不可修改地址,如对象 4、final 修饰的方法不能被重写 5、final 修饰的类不能被继承
package com.fc.j._final; public class FinalDemo1 { public static void main(String[] args) { // 测试final修饰的修饰的变量 final int num; num = 10; System.out.println(num); // num = 20; } } // final修饰的类不能被继承,断子绝孙 class Father { // final int age; final int age = 16; public final void play() { System.out.println("下棋"); } } class Son extends Father { // @Override // public final void play() { // // } }特点
final修饰可以保证安全性,比如数组的长度属性,String类,这些都是final修饰的,保证不可变
总结abstract 关键字【抽象类】1、final表示最终的,可以修饰变量,属性和方法
2、final修饰的基本数据类型的成员变量只能被赋值一次
3、final修饰的引用数据类型的成员变量地址不可变,但不影响地址所指向的对象的 *** 作
4、final修饰的方法不能被重写
5、final修饰的类不能被继承
不能实例化的类就是抽象类,用 abstract 修饰
构成abstract class 类名 { 成员变量 构造方法 成员方法 非抽象方法 抽象方法 }要求
1、抽象类和抽象方法必须用关键字 abstract 修饰 2、抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类 3、abstract 修饰的方法没有方法体,且子类必须重写 4、抽象类不能实例化,因为 abstract 类中有 abstract 方法 5、抽象类的子类 也可以是一个抽象类,可以重写也可以不重写父类的抽象方法。 可以是一个具体类。这个类必须重写抽象类中的所有抽象方法。(可以实例化)
public class TestSon { public static void main(String[] args) { Son son = new Son(); son.play(); } } // 抽象类 abstract class Father { String name; int age; public Father() { } public void eat() { System.out.println("吃饭"); } // 抽象方法 abstract public void play(); } class Son extends Father { // 抽象方法的重写 @Override public void play() { System.out.println("玩游戏"); } }static 关键字 概述
static 关键字方便在没有创建对象的情况下来进行调用方法和变量(优先级高于对象),可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能
static 变量static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
public class PersonDemo { public static void main(String[] args) { Person person1 = new Person("张三", 16); Person person2 = new Person("李四", 17); Person person3 = new Person("王五", 18); Person person4 = new Person("赵六", 19); System.out.println("姓名:" + person1.name + " 年龄:" + person1.age + " 地址:" + Person.address); System.out.println("姓名:" + person2.name + " 年龄:" + person2.age + " 地址:" + Person.address); System.out.println("姓名:" + person3.name + " 年龄:" + person3.age + " 地址:" + Person.address); System.out.println("姓名:" + person4.name + " 年龄:" + person4.age + " 地址:" + Person.address); // 通过类名直接调用static修饰的成员变量,此时是没有对象的 System.out.println("没有对象:" + Person.address); // System.out.println("没有对象:" + Person.name); // person1.testStatic(); // 通过类名直接调用静态方法 Person.testStatic(); } }总结
1、通过类名调用静态成员变量,因为静态变量与对象无关 2、静态变量被所有对象共享,一处更改处处更改static方法
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
public class Person { public String name = "张三"; public int age; public static String address = "郑州"; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } // 自定义static修饰的成员方法 public static void testStatic() { // test(); System.out.println("static mothed"); // this.name; } public void test() { System.out.println("method"); } }总结
1、static修饰的方法不能访问本类中的非静态变量和方法,不能使用this 2、通过类名来调用静态成员方法,工具类的应用很广泛总结
代码块1、static修饰的成员变量和方法都是对象所共享的资源,对其进行的 *** 作会作用于所有对象。
2、static修饰的成员变量和方法依赖于类不依赖于对象,即没有对象
3、static修饰的成员变量和成员方法都可以通过类名调用,没有对象
4、静态不能调用非静态,不能使用this关键字,没有对象
5、静态成员变量常和final关键字搭配作为常量使用,静态方法常用于工具类
在 Java 中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程)
构造代码块格式: { }注意
1、用于给对象初始化,多个构造方法中相同的代码存放到一起,每次调用构造方法都会执行,并且在构造方法前执行 2、只有创建对象时调用,类不能调用 3、构造代码块可以有多个,建议只写一个
class Person { { System.out.println("Person构造代码块执行"); } public Person() { System.out.println("Person构造方法执行"); } } public class TestPerson { public static void main(String[] args) { System.out.println("main方法"); new Person(); new Person(); } }静态代码块
格式: static { }注意
1、用于给类进行初始化,在加载的时候就执行,并且只执行一次 2、优先级高于主函数 3、静态代码块可以有多个,顺序执行,建议只写一个
class Person { static { System.out.println("Person静态代码块执行"); } public Person() { System.out.println("Person构造方法执行"); } } public class TestPerson { static { System.out.println("静态代码块1执行"); } public static void main(String[] args) { System.out.println("main方法"); new Person(); new Person(); } static { System.out.println("静态代码块2执行"); } }
结果
静态代码块1执行 静态代码块2执行 main方法 Person静态代码块执行 Person构造方法执行 Person构造方法执行代码块相关执行顺序
public class Father { public Father() { System.out.println("父类构造方法执行~~~"); } { System.out.println("父类构造代码块执行~~~"); } static { System.out.println("父类静态代码块执行~~~"); } public static void function() { System.out.println("父类静态成员方法执行~~~"); } } public class Son extends Father{ public Son() { System.out.println("子类构造方法执行~~~"); } { System.out.println("子类构造代码块执行~~~"); } static { System.out.println("子类静态代码块执行~~~"); } public static void function() { System.out.println("子类静态成员方法执行~~~"); } public static void main(String[] args) { System.out.println("main方法执行~~~"); new Son(); } }
结果
父类静态代码块执行~~~ 子类静态代码块执行~~~ main方法执行~~~ 父类构造代码块执行~~~ 父类构造方法执行~~~ 子类构造代码块执行~~~ 子类构造方法执行~~~面试题
执行顺序
public class Test { static Test test1 = new Test(); static Test test2 = new Test(); static { System.out.println("静态代码块"); } { System.out.println("构造代码块"); } public Test() { System.out.println("构造方法"); } public static void main(String[] args) { System.out.println("main方法"); new Test(); } }
结果
构造代码块 构造方法 构造代码块 构造方法 静态代码块 main方法 构造代码块 构造方法总结
接口 概念1、构造代码块用于给对象初始化,每次创建对象都会调用构造代码块,并且执行顺序在构造方法之前
2、静态代码块用于给类初始化,当类被加载的时候就会调用静态代码块(只执行一次),执行顺序在main方法之前
接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
格式interface :用来声明接口的关键字
声明格式: interface 接口名 { 静态常量; 抽象方法; } 实现格式: class 类名 implements 接口名 { }特点
1、接口中的成员变量只能是静态常量,定义时必须初始化。默认修饰符:public static final 2、接口中没有构造方法,因为接口不能实例化对象 3、接口中的成员方法只能是抽象方法,没有方法体。默认修饰符:public abstract 4、接口的实现类必须重写接口中方法,或者是一个抽象类(可以重写也可以不重写接口中的方法)接口的声明和实现
interface play{ // 常量,缺省修饰符:public static final int time = 10; // 抽象方法,缺省修饰符:public abstract void geme(); } public class TestInterface3 implements play{ // 重写接口中的方法 @Override public void geme() { System.out.println("玩游戏"); } }
【注意】接口的实现类必须重写接口中的方法
抽象类实现接口interface servlet { void init(); void service(); } abstract class baseServlet implements servlet { // 重写init()方法 @Override public void init() { System.out.println("初始化"); } } class MyServlet extends baseServlet { @Override public void service() { System.out.println("服务方法"); } } public class Test { public static void main(String[] args) { new MyServlet().init(); new MyServlet().service(); } }
【注意】抽象类实现接口,可以选择性重写也可以不重写接口中的方法
类的接口多实现interface Play { void playGame(); } interface Eat { void eatNoodles(); } public class TestInterface3 implements Play, Eat { // 重写Play类中的方法 @Override public void playGame() { System.out.println("玩游戏"); } // 重写Eat类中的方法 @Override public void eatNoodles() { System.out.println("吃面条"); } }
【注意】接口的实现类必须重写所有接口中的方法
接口的继承interface Eat { void noodles(); } interface Play { void happy(); } // 单继承 interface Person extends Play { } // 多继承 interface Animal extends Play, Eat { } // 实体类实现Animal接口,重写所有方法 class Biology implements Animal { @Override public void happy() { System.out.println("玩得开心"); } @Override public void noodles() { System.out.println("面条好吃"); } } public class Test { public static void main(String[] args) { Biology biology = new Biology(); biology.happy(); // 玩得开心 biology.noodles(); // 面条好吃 } }
【注意】接口之间可以单继承,也可以多继承
jdk1.8新特性:default关键字在接口中使用interface Function { void test(); default void testDefault() { System.out.println("default修饰的方法可以有方法体"); } } // default 修饰的接口可以不被重写 class base implements Function { @Override public void test() { System.out.println("base类重写Function接口中的方法"); } } // default 修饰的接口也可以重写 class Boost implements Function { @Override public void test() { System.out.println("Boost类重写Function接口中的方法"); } @Override public void testDefault() { System.out.println("Boost类重写Function接口中的default方法"); } } public class TestInterface2 { public static void main(String[] args) { base base = new base(); Boost boost = new Boost(); base.test(); // base类重写Function接口中的方法 base.testDefault(); // default修饰的接口可以有方法体 boost.test(); // Boost类重写Function接口中的方法 boost.testDefault();// Boost类重写Function接口中的default方法 } }
【注意】default修饰的接口可以不被重写
总结1、接口是对类的扩展,通过接口可以让类拥有更多更强的功能 2、接口中只有静态常量和抽象方法,所以不能实例化 3、接口的实现类必须重写所有方法,或者是个抽象类 4、接口可以多实现 5、接口可以单继承,也可以多继承 6、JDK1.8新特性,接口中被default修饰的方法,必须有方法体,且实现类不强制要求重写 7、JDK1.8新特性,接口中被static修饰的方法,必须有方法体,且子类和实现类不能重写多态
二者具有直接或间接的继承关系时,父类引用指向子类对象,从而产生多种形态;接口的引用指向实现接口的类对象也是多态
特点多态场景下,父类引用调用方法,如果被子类重写过,优先执行子类重写过后的方法
public class TestCar { public static void main(String[] args) { // 父类引用指向子类对象 Vehicle vehicle = new Car(); // 优先执行子类重写过的方法 vehicle.run(); // Car run!!! } } class Vehicle { public void run() { System.out.println("Vehicle run!!!"); } } class Car extends Vehicle { @Override public void run() { System.out.println("Car run!!!"); } }应用场景一
使用父类作为方法形参实现多态,使方法参数的类型更为宽泛
public class TestCar { public static void main(String[] args) { Vehicle vehicle = new Car(); vehicle.type = "小汽车"; Bike bike = new Bike(); bike.type = "自行车"; Bus bus = new Bus(); bus.type = "公交车"; Employee employee = new Employee("你的迪丽热巴"); employee.goHome(vehicle); employee.goHome(bus); } } class Employee { String name; public Employee() { } public Employee(String name) { this.name = name; } public void goHome(Vehicle vehicle) { System.out.println(this.name + "乘坐" + vehicle.type + "交通工具回家"); } } class Vehicle { String type; public void run() { System.out.println("Vehicle run!!!"); } } class Bus extends Vehicle { @Override public void run() { System.out.println("Bus run!!!"); } } class Car extends Vehicle { @Override public void run() { System.out.println("Car run!!!"); } } class Bike extends Vehicle { @Override public void run() { System.out.println("Bike run!!!"); } }
结果
你的迪丽热巴乘坐小汽车回家 Car run!!! 你的迪丽热巴乘坐公交车回家 Bus run!!!应用场景二
使用父类作为方法返回值实现多态,使方法可以返回不同子类对象
public Vehicle buyVehicle(int money) { Vehicle vehicle = null; if (money >= 100) { Bus bus = new Bus(); bus.speed = 60; bus.price = 1230000.0; bus.seatNum = 16; bus.type = "公交车"; vehicle = bus; } else if (money >= 30) { Car car = new Car(); car.price = 310000.0; car.speed = 90; car.type = "小汽车"; car.brand = "BMW"; vehicle = car; } else if (money >= 1) { Bike bike = new Bike(); bike.type = "捷安特自行车"; bike.speed = 40; bike.price = 2000.0; bike.color = "红色"; vehicle = bike; } return vehicle; }向上装箱与向下拆箱
class Animal{} class Cat extends Animal{} class Dog extends Animal{} class Fish extends Animal {} public class Test { public static void main(String[] args) { showAnimal(new Animal()); // code.polymorphic.animal.Animal@7852e922 // 向上转型 showAnimal(new Cat()); // code.polymorphic.animal.Cat@4e25154f // 向上转型 showAnimal(new Dog()); // code.polymorphic.animal.Dog@70dea4e // 向上转型 showAnimal(new Fish()); // code.polymorphic.animal.Fish@5c647e05 System.out.println("----------------------"); Animal animal = getAnimal(); // 向下转型 Cat cat = (Cat) getCat(); // 向下转型 Dog dog = (Dog) getDog(); // 向下转型 Fish fish = (Fish) getFish(); System.out.println(animal); // code.polymorphic.animal.Animal@33909752 System.out.println(cat); // code.polymorphic.animal.Cat@55f96302 System.out.println(dog); // code.polymorphic.animal.Dog@3d4eac69 System.out.println(fish); // code.polymorphic.animal.Fish@42a57993 } public static void showAnimal(Animal animal) { System.out.println(animal); } public static Animal getAnimal() { return new Animal(); } public static Animal getCat() { return new Cat(); } public static Animal getDog() { return new Dog(); } public static Animal getFish() { return new Fish(); } }instanceof 关键字
用于判断当前对象是否是某个类,或者其子类、实现类的实例。如果是返回true,否则返回false。
class Animal { } class Tiger extends Animal { } class Panda extends Animal { } class Monkey extends Animal { } public class AnimalDemo { public static void main(String[] args) { Animal ani = getAnimal(); if (ani instanceof Panda) { // ani一定是panda对象或子类对象 Panda panda2 = (Panda) ani; System.out.println("这是熊猫:" + panda2); showPanda(panda2); } else { System.out.println("这是动物:" + ani); } } // 获取动物,返回一个Panda对象 public static Animal getAnimal() { return new Panda(); } // 展示熊猫对象 public static void showPanda(Panda panda) { System.out.println(panda); } }
【注意】使用 instanceof 关键字做判断时, instanceof *** 作符的左 *** 作数必须和右 *** 作类或者接口存在继承或实现关系
总结【补充】内部类 概述1、什么是什么
2、父类引用指向子类对象,接口引用指向实现类对象
3、instanceof 用以比较对象是否是类或父类的实例,接口的实现类
Java 编程语言允许在一个类中定义另一个类。这样被定义的类称为嵌套类(Nested Class),通常情况下我们称之为内部类(Inner Class)。
为什么要使用内部类为了解决多继承的问题
案例代码接口
public interface Learnable { void learnForeignLanguage(); }
Student 外部类,内部类实现接口
public class Student { private int id; private String name; // 内部类实现接口并重写方法 class JapaneseCourse implements Learnable { @Override public void learnForeignLanguage() { System.out.println("学日语"); } } // 内部类实现接口并重写方法 class KoreanCourse implements Learnable { @Override public void learnForeignLanguage() { System.out.println("学韩语"); } } // 外部类中的方法 public void learnForeignLanguage() { System.out.println("学英语"); } // Constructors、Setters、Getters }
测试类
public class Test { public static void main(String[] args) { Student student = new Student(); student.learnForeignLanguage(); } }
结果
学英语 学韩语 学日语分类【重点】 特点【重点】
- 非静态嵌套类可以访问外部类的其他成员,包括被 private 修饰的成员。
- 静态嵌套类不能访问外部类的其他成员,除非是静态成员。
- 作为类的成员,嵌套类可以声明为 private、public、protected 等,区别于外部类只能声明为public 或者 private
定义在外部类中,与成员变量、成员方法同一级别的类。
格式// 成员内部类的声明格式 // 外部类 class OuterClass { // 内部类 class InnerClass { } } // 成员内部类对象的创建 // 外部类类名.内部类类名 对象名 = new 外部类构造方法.内部类构造方法 OuterClass.InnerClass innerObject = new OuterClass().newInnerClass();特点
- 在内部类中可以访问其外部类的所有域,即使是私有的。即外部类对内部类可见。
- 在外部类中可以创建内部类的对象,即使内部类是私有的。即内部类对其所在的外部类可见,对外不可见。
声明成员内部类
【注意】
- 成员内部类的声明位置在类中,与外部类的成员变量和成员方法的级别相同,都属于外部类的成员。
- 成员内部类中可以声明成员变量和成员方法以及构造方法。
- 成员内部类中不能包含静态成员
// 外部类 class OuterClass { // 成员变量 public int field1 = 1; // 成员方法 public void method1() { System.out.println("OuterClass Method1"); } // 外部类构造方法 public OuterClass() { System.out.println("OuterClass Constructor"); } // 内部类,定义在类中,与外部类的成员属于同一级别 class InnerClass { // 内部类的成员变量和成员方法 public int field2 = 2; public void method2() { System.out.println("InnerClass Method2"); } // 内部类构造方法 public InnerClass() { System.out.println("InnerClass Constructor"); } } }
创建内部类的对象
【注意】
- 内部类对象的声明必须使用外部类类名.内部类类名的形式,不能直接访问。
- 内部类对象的创建必须借助外部类的对象,及要实例化内部类,必须首先实例化外部类。
- 内部类对象和普通的类对象一样可以调用自身的成员变量和方法。
public class Test { public static void main(String[] args) { // 创建内部类对象的两种方式 // 1、先创建外部类对象,再创建内部类对象 OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass1 = outerClass.new InnerClass(); // 调用内部类的成员变量和成员方法 System.out.println(innerClass1.field2); // 2 innerClass1.method2(); // InnerClass Method2 // 2、使用外部类的匿名对象直接创建内部类对象 OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass(); // 调用内部类的成员变量和成员方法 System.out.println(innerClass2.field2); // 2 innerClass2.method2(); // InnerClass Method2 } }案例代码二
在内部类中可以访问其外部类的所有域,即使是私有的。即外部类对内部类可见。
// 外部类 class OuterClass { // 成员变量 public int field1 = 1; // 私有化成员变量 private String privateField = "OuterPrivateField"; // 私有化成员方法 private void method1() { System.out.println("OuterClass private Method1"); } // 内部类,定义在类中,与外部类的成员属于同一级别 class InnerClass { // 内部类的成员变量和成员方法 public int field2 = 2; public void method2() { // 内部类可以直接访问外部类中的成员(包括私有化) System.out.println("OuterClass Field:" + field1); System.out.println("OuterClass Field:" + privateField); method1(); } } }
测试类
// 测试类 public class Test { public static void main(String[] args) { // 使用外部类的匿名对象直接创建内部类对象 OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass(); // 调用内部类的成员变量和成员方法 innerClass2.method2(); } }
结果
OuterClass Field:1 OuterClass Field:OuterPrivateField OuterClass private Method1案例代码三
在外部类中可以创建内部类的对象,即使内部类是私有的。即内部类对其所在的外部类可见。
// 外部类 class OuterClass { // 成员变量 public int field1 = 1; // 外部类可以创建内部类的对象(包括私有化) private InnerClass innerClass = new InnerClass(); // 成员方法 public void method1() { System.out.println("OuterClass Method1"); // 外部类可以通过内部类对象直接调用内部类的私有化成员变量和成员方法 System.out.println("InnerClass Field:" + innerClass.field2); innerClass.method2(); } // 内部类,定义在类中,与外部类的成员属于同一级别 private class InnerClass { // 内部类的私有化成员变量和成员方法 private int field2 = 2; private void method2() { System.out.println("InnerClass Method"); } } }
私有化的内部类只有外部类能够访问。
// 测试类 public class Test { public static void main(String[] args) { // 私有化内部类,外界无法创建对象 // OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass(); OuterClass outerClass = new OuterClass(); outerClass.method1(); } }静态内部类 概述
被静态修饰的成员内部类就是静态内部类。
格式// 成员内部类的声明格式 // 外部类 class OuterClass { // 内部类 static class InnerClass { int field; public void method() {} static int staticField; public static void staticMethod() {} } }特点
- 静态内部类中无法直接引用到其所在的外部类中的非静态成员变量和方法。
- 外部类必须通过类名或者对象的形式访问静态内部类中的成员。
【注意】
- 静态内部类可以直接访问外部类中的静态成员。
- 静态内部类必须通过对象访问外部类中的非静态成员。
// 外部类 class OuterClass { // 外部类非静态成员 public int outerField = 1; public void outerMethod() { System.out.println("OuterMethod"); } // 外部类静态成员 public static int outerStaticField = 2; public static void outerStaticMethod() { System.out.println("outerStaticMethod"); } // 内部类,定义在类中,与外部类的成员属于同一级别 static class InnerClass { public void innerMethod() { // 静态内部类可以直接访问外部类中的静态成员 System.out.println("outerStaticField:" + outerStaticField); outerStaticMethod(); // 静态内部类可以通过外部类的对象访问其非静态成员 OuterClass outerClass = new OuterClass(); System.out.println(outerClass.outerField); outerClass.outerMethod(); } } }案例代码二
【注意】
- 静态内部类中可以有静态成员和非静态成员。
- 外部类可以通过类名直接访问静态内部类中的静态成员。
- 外部类必须通过对象访问静态内部类中的非静态成员。
静态内部类
// 外部类 class OuterClass { // 内部类对象 private InnerClass innerClass = new InnerClass(); public void OuterMehtod() { // 外部类无法直接访问静态内部类中的静态成员,必须通过类名.的形式 System.out.println("staticField:" + InnerClass.staticField); InnerClass.staticMethod(); // 可以通过创建内部类对象的形式访问静态内部类中非静态成员 System.out.println("innerField:" + innerClass.innerField); innerClass.innerMethod(); } // 内部类,定义在类中,与外部类的成员属于同一级别 static class InnerClass { public int innerField = 1; public void innerMethod() { System.out.println("InnerClass Method"); } // 内部类的成员变量和成员方法 public static int staticField = 2; public static void staticMethod() { System.out.println("InnerClass static Method"); } } }
测试类
【注意】外界无法直接访问静态内部类中的非静态成员。
// 测试类 public class Test { public static void main(String[] args) { // 静态内部类中的静态成员变量和方法可以通过类名直接访问 System.out.println("staticField:" + InnerClass.staticField); InnerClass.staticMethod(); // 静态内部类中的非静态方法对外无法直接进行访问。 new OuterClass().OuterMehtod(); } }局部内部类 概述
定义在方法中的内部类就是局部内部类。
格式// 外部类 class OuterClass { // 外部类中的方法 public void method() // 内部类 class InnerClass { } } }特点
- 局部内部类只作用其被声明的方法中有效,对外无法进行访问。
- 局部内部类中可以无条件访问外部类中的成员。
- 局部内部类不能被有访问修饰符,但是可以被 abstract 或者 final 修饰。
- 局部内部类中不能含有静态成份。
- 局部内部类只能访问其所在的方法中被 final 修饰的局部变量。
【注意】
- 局部内部类只能在其所在的方法体中创建对象。
- 局部内部类中可以无条件访问外部类中的成员。
// 外部类 class OuterClass { // 外部类中的成员变量 public String outerField = "outerField"; private String privateOuterField = "privateOuterField"; public static String staticOuterField = "staticOuterField"; public void method() { class InnerClass { public void innerMethod() { // 局部内部类可以访问外部类中的任意成员 System.out.println(outerField); System.out.println(privateOuterField); System.out.println(staticOuterField); } } // 局部内部类只能在当前方法内部创建对象 InnerClass innerClass = new InnerClass(); innerClass.innerMethod(); } }
测试类
public class Test { public static void main(String[] args) { OuterClass outerClass = new OuterClass(); outerClass.method(); } }案例代码二
【注意】
- 局部内部类不能被有访问修饰符,但是可以被 abstract 或者 final 修饰。
- 局部内部类只能访问其所在的方法中被 final 修饰的局部变量。
// 外部类 class OuterClass { public void method() { // 局部变量 int num = 1; // 局部内部类不能有访问修饰符,只能被 abstract 或者 final 修饰 class InnerClass { public void innerMethod() { // 局部内部类只能访问所在方法中被 final 修饰的局部变量 // System.out.println(num++); System.out.println(num); } } } }匿名内部类【重点】 概述
匿名类会使代码更简洁。使用匿名类可同时声明和实例化类。匿名类没有类名,其他与局部类类似。如果局部类只使用一次,则推荐使用匿名类。
格式所谓匿名内部类,其本质就是继承了该类或者实现了该接口的子类对象。
// 接口 interface InnerInterface() { void innerMethod(); } // 外部类 public class OuterClass { public void outerMethod() { // 匿名内部类 InnerInterface innerInterface = new InnerInterface() { // 实现接口中的抽象方法 @Override public void innerMethod() { } }; } }特点
- 匿名内部类是定义在表达式中的,所以它必须是语句的组成部分(不能省略右大括号之后的分号)。
- 匿名内部类中不能声明构造方法(没有类名)。
- 抽象类的匿名内部类在实例化时可以给构造方法传递参数。
- 匿名内部类只能访问其所在的方法中被 final 修饰的局部变量。
接口
// 接口 public interface InnerInterface { // 抽象方法 void innerMethod(); }
【注意】匿名内部类可以声明成员变量和方法,但一般只用做接口回调,即只重写抽象方法。
匿名内部类
// 外部类 public class OuterClass { // 外部类成员方法 public void outerMethod() { // 匿名内部类对象 InnerInterface innerInterface = new InnerInterface() { // 重写抽象方法 @Override public void innerMethod() { System.out.println("InnerMethod"); } }; innerInterface.innerMethod(); } }案例代码二
抽象类
// 抽象类 public abstract class InnerClass { // 带参构造方法 public InnerClass(String arg) { System.out.println("Constructor param:" + arg); } // 抽象方法 abstract void innerMethod(); }
匿名内部类
如果抽象类中有带参构造方法,其匿名内部类在声明时可以携带参数
public class OuterClass { public void outerMethod() { // 抽象类的匿名内部类在声明时可以携待参数 InnerClass innerClass = new InnerClass("arg") { @Override void innerMethod() { System.out.println("InnerClass Method"); } }; innerClass.innerMethod(); } }案例代码三
接口
// 接口 public interface InnerInterface { // 抽象方法 void innerMethod(); }
匿名内部类
【注意】匿名内部类只能访问其所在的方法中被 final 修饰的局部变量。
// 外部类 public class OuterClass { private int field = 1; // 外部类成员方法 public void outerMethod() { int num = 1; // 匿名内部类对象 InnerInterface innerInterface = new InnerInterface() { // 重写抽象方法 @Override public void innerMethod() { // 局部内部类能够任意调用外部类中的成员 System.out.println("Field:" + field++); // 匿名内部类只能调用final修饰的局部变量 System.out.println("Local Variable:" + num); } }; innerInterface.innerMethod(); } }优点
-
对单一用途的类进行逻辑分类
-
内部类可以独立继承一个接口,不受外部类是否继承接口影响
-
增加了封装性
-
可以使代码更具可读性和可维护性
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把方法作为一个方法的参数(方法作为参数传递进方法中)。
特点【重点】Lambda 表达式是一个匿名方法
使用 Lambda 表达式简化了匿名内部类的使用,语法更加简单,可以使代码变的更加简洁紧凑。
语法规则// parameters代表方法的入参,可以是0个1个或者多个 // statements代表方法体 (parameters...) -> { statements; }
- **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。 【建议加上】
- **可选的参数小括号:**一个参数无需定义小括号,但多个参数需要定义小括号。 【建议加上】
- **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。 【建议加上】
- **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
- 方法的参数或局部变量类型必须为接口
- 接口中有且仅有一个抽象方法
-
所需类型不一样
1、匿名内部类的类型可以是 类,抽象类,接口
2、Lambda表达式需要的类型必须是接口
-
抽象方法的数量不一样
1、匿名内部类所需的接口中的抽象方法的数量是随意的
2、Lambda表达式所需的接口中只能有一个抽象方法
-
实现原理不一样
1、匿名内部类是在编译后形成一个class
2、Lambda表达式是在程序运行的时候动态生成class
部类
InnerInterface innerInterface = new InnerInterface() {
// 实现接口中的抽象方法
@Override
public void innerMethod() {
}
};
}
}
#### 特点 1. 匿名内部类是定义在表达式中的,所以它必须是语句的组成部分(不能省略右大括号之后的分号)。 2. 匿名内部类中不能声明构造方法(没有类名)。 3. 抽象类的匿名内部类在实例化时可以给构造方法传递参数。 4. 匿名内部类只能访问其所在的方法中被 final 修饰的局部变量。 #### 案例代码一 接口 ```java // 接口 public interface InnerInterface { // 抽象方法 void innerMethod(); }
【注意】匿名内部类可以声明成员变量和方法,但一般只用做接口回调,即只重写抽象方法。
匿名内部类
// 外部类 public class OuterClass { // 外部类成员方法 public void outerMethod() { // 匿名内部类对象 InnerInterface innerInterface = new InnerInterface() { // 重写抽象方法 @Override public void innerMethod() { System.out.println("InnerMethod"); } }; innerInterface.innerMethod(); } }案例代码二
抽象类
// 抽象类 public abstract class InnerClass { // 带参构造方法 public InnerClass(String arg) { System.out.println("Constructor param:" + arg); } // 抽象方法 abstract void innerMethod(); }
匿名内部类
如果抽象类中有带参构造方法,其匿名内部类在声明时可以携带参数
public class OuterClass { public void outerMethod() { // 抽象类的匿名内部类在声明时可以携待参数 InnerClass innerClass = new InnerClass("arg") { @Override void innerMethod() { System.out.println("InnerClass Method"); } }; innerClass.innerMethod(); } }案例代码三
接口
// 接口 public interface InnerInterface { // 抽象方法 void innerMethod(); }
匿名内部类
【注意】匿名内部类只能访问其所在的方法中被 final 修饰的局部变量。
// 外部类 public class OuterClass { private int field = 1; // 外部类成员方法 public void outerMethod() { int num = 1; // 匿名内部类对象 InnerInterface innerInterface = new InnerInterface() { // 重写抽象方法 @Override public void innerMethod() { // 局部内部类能够任意调用外部类中的成员 System.out.println("Field:" + field++); // 匿名内部类只能调用final修饰的局部变量 System.out.println("Local Variable:" + num); } }; innerInterface.innerMethod(); } }优点
-
对单一用途的类进行逻辑分类
-
内部类可以独立继承一个接口,不受外部类是否继承接口影响
-
增加了封装性
-
可以使代码更具可读性和可维护性
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把方法作为一个方法的参数(方法作为参数传递进方法中)。
特点【重点】Lambda 表达式是一个匿名方法
使用 Lambda 表达式简化了匿名内部类的使用,语法更加简单,可以使代码变的更加简洁紧凑。
语法规则// parameters代表方法的入参,可以是0个1个或者多个 // statements代表方法体 (parameters...) -> { statements; }
- **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。 【建议加上】
- **可选的参数小括号:**一个参数无需定义小括号,但多个参数需要定义小括号。 【建议加上】
- **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。 【建议加上】
- **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
- 方法的参数或局部变量类型必须为接口
- 接口中有且仅有一个抽象方法
-
所需类型不一样
1、匿名内部类的类型可以是 类,抽象类,接口
2、Lambda表达式需要的类型必须是接口
-
抽象方法的数量不一样
1、匿名内部类所需的接口中的抽象方法的数量是随意的
2、Lambda表达式所需的接口中只能有一个抽象方法
-
实现原理不一样
1、匿名内部类是在编译后形成一个class
2、Lambda表达式是在程序运行的时候动态生成class
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)