一. 面向对象
1. 导学
- 面向对象: 关注现实存在的事物的各方面的信息, 从对象的角度出发,根据事物的特征进行程序设计.
面向对象开发的优势
- 稳定性
- 可扩展性
- 可重用性
2. 类和对象
- 对象: 用来描述客观事物的一个实体
- 类: 具有相同属性和方法的一组对象的集合
3. 创建类
package com.imooc.animal;
public class Cat{
// 成员属性: 昵称,年龄,体重,品种
String name;
int month;
double weight;
String species;
// 方法: 跑动,吃东西
public void run(){
System.out.println("小猫快跑");
}
public void eat(){
System.out.println("小猫吃鱼");
}
}
4. 实例化对象
- 在包下重新建立一个类
- 然后实例化
package com.imooc.animal;
public class CatTest(){
public static void main(String[] args){
//对象实例化
Cat one = new Cat();
//调用 方法
one.eat();
one.run();
}
}
-
成员属性就算没有 赋值,也是有 默认初始值的. 如下:
-
字符串类型的默认初始值为 null
-
整型的默认初始值为 0
-
浮点型的默认初始值为 0.0
-
具体如下:
实例化对象的过程可以分为两部分
- 声明对象 Cat one;
- 实例化对象 new Cat();
5. 单一职责原则
- 又叫 单一职责原则
有且只有一个引起功能变化的原因!
6. new关键字
- 通过 new 来实例化类.
7. 构造方法–无参构造方法
- 构造方法又叫 构造函数,构造器
- 必须搭配new使用,不能被对象单独调用!
构造方法
- 构造方法与类同名且没有返回值! 必须和类名一样!!
- 构造方法的语句格式
- 构造方法 只能 在对象实例化的时候调用
- 当没有指定构造方法时, 系统会自动添加无参的构造方法
- 当有指定构造方法,无论是有参,无参的构造方法,都不会自动添加无参的构造方法
- 一个类中可以有多个构造方法
8. 构造方法–带参构造方法
public class Cat{
String name;
int month;
double weight;
String species;
public Cat(){
}
// 带参的构造函数
public Cat(String name, int month, double weight, String species){
this.name = name;
this.month = month;
this.weight = weight;
this.species = species;
}
}
实例化代码 简化如下
Cat one = new Cat("花花", 2, 1000, "英国短毛猫");
9. this关键字
上述第8条的带参构造函数中, 我们用了 this. 代表的当前对象(谁调用它就是谁).
this的使用
- 调用成员属性(同一个类中的),解决成员属性和局部变量同名冲突
- 调用成员方法(同一个类中的)
- 调用重载的构造方法 (例如在 有参构造中,调用 this() 表示调用无参构造方法) 注意: 通过this()调用构造方法,必须放在方法体的第一行!
10. 构造方法调用
- 类中普通的方法名,不要用 类名. 类名最好只用于 构造方法.
- 可以在 类中的有参构造函数中,调用 this(); 来调用 无参的构造方法!
二. 封装
1. 封装的概念和特点
- 将类的某些信息隐藏在类内部,不允许外部程序直接访问
- 通过该类提供的方法来实现对隐藏信息的操作和访问
- 隐藏对象的信息
- 留出访问的接口
特点:
- 只能通过规定的方法访问数据
- 隐藏类的实例细节,方便修改和实现
2. 封装的代码实现
实现步骤
- 修改属性的可见性, 访问修饰符修改为 private (私有,只允许在类内访问).
- 创建 getter/setter 方法, 访问修饰符设置为 public, 用于属性的读写.
- 在 getter/setter 方法中加入属性控制语句.
public class Cat{
...
// 修改属性的可见性,修改为 私有 private, 限定只能在 当前类中访问,类外不允许访问!
private String name;
// 设置共有的 get/set 方法,供 外部调用!
// 在 get/set方法中可以添加 属性的限定
public void setName(String name){
// 可以添加限制的代码
/*
if( name == ''){
System.out.println("姓名不能为空!");
}
*/
this.name = name;
}
public String getName(){
// 可以添加限制的代码
/*
return "小猫的名字为:" + this.name;
*/
return this.name;
}
...
}
调用时代码如下
public class CatTest{
public static void main(String[] args){
Cat one = new Cat();
one.setName("凡凡");
...
System.out.println("昵称: " + one.getName());
...
}
}
- eclipse 中快捷添加 getter/setter方法: 鼠标将光标点击到 合适位置,然后右键,Source / Generate Getter and Setter … 即可选择添加.
- 注意: 如果使用了 getter/setter 方法, 那么在有参构造方法中, 赋值的话, 也推荐用 setter 方法.
3. 使用包进行类管理
1. 定义包
语法
- package 包名;
- 例: package com.imooc.animal;
注意:
- 必须放在java源文件的第一行
- 一个Java源文件中只能有一个 package 语句
- 包名全部英文小写
- 命名方式: 域名倒序+模块+功能
2. 导入包
语法:
- import 包名.类名;
- 例: 导入包中全部类: import com.imooc.*
- 例: 导入包中指定类: import com.imooc.animal.Cat;
实例演示如下:
Test.java 中调用 Cat, 那么如何指定是 哪个 Cat呢?
解决方法
- 导包, 通过 import 导入所有类,如下代码所示
package com.imooc.newtest;
import com.imooc.animal.*; //加载 com.imooc.animal 下面的所有类
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
CatTest test = new CatTest();
}
}
- 也是导包, 通过 import 导入包下的特定类
package com.imooc.newtest;
import com.imooc.animal.Cat; //加载 com.imooc.animal 下面的 Cat 类
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
}
}
- 直接在程序中, 直接加载 com.imooc.animal.Cat 这样的方式
package com.imooc.newtest;
public class Test {
public static void main(String[] args) {
com.imooc.animal.Cat cat = new com.imooc.animal.Cat();
}
}
如果 想同时引用这2个Cat, 注意,要用如下写法:
package com.imooc.newtest;
import com.imooc.animal.Cat;
import com.imooc.mechanics.*; //上面有 Cat了,这里只能这样写
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
com.imooc.mechanics.Cat two = new com.imooc.mechanics.Cat();
}
}
如上, 第二个 Cat, 就必须用 包名这样的形式来区别开.
4. static关键字
- static: 静态 静态成员,类成员
static 可以加在 成员属性和成员方法前. 意义如下:
- static + 成员属性 就变成了 静态属性/类属性 (所有对象都共享)
- static + 成员方法 就变成了 静态方法/类方法 (所有对象都共享)
static 特点:
- 类对象共享
- 类加载时产生,销毁时释放,生命周期长
静态成员访问方式有2种:
- 对象.成员
- 类.成员
类方法访问和静态成员访问的方式类似,也是2种
- 对象.成员方法()
- 类.成员方法() 推荐这种方式!
静态方法(类方法)书写的时候,也有地方需要注意:
- 在成员方法中, 是可以直接访问类的静态成员的
- 在 静态方法(类方法)中 不能直接访问同一个类中的 非静态成员(属性+方法), 只能直接调用 同一个类 中的静态成员(属性+方法).
- 静态方法(类方法) 中,不能使用 this.
- 在静态方法中,如果想使用非静态成员,可以实例化一个对象,然后通过对象去调用非静态成员.
5. 代码块
- 一对 大括号对 {} 就是一个代码块
- 在 方法中的时候,就叫做 普通代码块, 执行的顺序和一般语句一样,顺序执行. 可以有多个.
- 在 类中出现的时候(和成员属性,成员方法等同级存在), 就叫做 构造代码块. 运行顺序: 在构造函数之前. 每个对象实例化都会执行.可以有多个.
- 在 构造代码块前面加上 static, 就叫做 静态代码块. 执行顺序: 静态代码块 在 构造代码块之前执行. 类加载的时候,就会执行. 多个对象实例化,也只会执行1次. 可以有多个.
- 普通代码块,构造代码块,静态代码块. 都可以有多个. 执行的顺序是 静态代码块 > 构造代码块 > 构造方法 > 普通代码块.
- 静态代码块 只能操作类中的静态成员. 如果想操作非静态成员,可以通过实例化来实现. 多个对象实例化,只会在第一次实例化的时候,调用.
- 构造代码块 可以操作类中的非静态成员.
三. java继承(上)
1. 继承的概念和特点
特点:
- 利于代码复用
- 缩短开发周期
继承:
- 一种类与类之间的关系
- 使用已存在的类的定义作为基础建立新类
- 新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择地继承父类
- 父类(超类/基类) — 子类(派生类)
继承的关系:
- 满足 “A is a B” 的关系就可以形成继承关系
2. 继承的实现
- 使用关键字 extends 来实现继承.
- java中的继承是 单继承, 1个子类只能有1个父类!
- 编写父类
class Animal{
//公共的属性和方法
}
- 编写子类,继承父类
class Dog extends Animal{
// 子类特有的属性和方法
}
class Cat extends Animal{
}
3. 方法的重写
语法规则
- 返回值类型
- 方法名
- 参数类型,顺序,个数
以上3点,必须和 父类 继承的方法相同.
方法重载:
- 同一个类中
- 方法名相同,参数列表不同(参数顺序,个数,类型)
- 方法返回值,访问修饰符任意
- 与方法的参数名无关
方法重写:
- 有继承关系的子类中
- 方法名相同,参数列表相同(参数顺序,个数,类型),方法返回值可以允许是子类类型
- 访问修饰符, 访问范围需要大于等于父类的访问范围
- 与方法的参数名无关
4. 方法重写的碎碎念
- 子类中也可以定义和父类同名的属性的!
5. 访问修饰符的分类及作用
访问修饰符
- 私有的: private 只允许在本类中进行访问
- 默认 允许在当前类,同包子类/非子类可以访问,但跨包子类/非子类都不允许访问.
- 受保护的: protected 允许在当前类,同包子类/非子类,跨包子类可以访问! 但跨包非子类不允许.
- 公有的: public 允许在任意位置访问
访问范围
6. 访问修饰符对方法重写的影响
- 方法重写的时候, 访问修饰符, 访问范围需要大于等于父类的访问范围
7. super关键字的使用
- super: 父类对象的引用
- 父类的构造方法不允许被继承,也不允许被重写. 可以 用 super() 来调用.
8. 继承的初始化顺序
- 父类静态成员->子类静态成员->父类对象构造->子类对象构造
9. super关键字的使用(下)
- 子类的构造的过程中必须调用其父类的构造方法
- 如果子类的构造方法没有显示标注,则系统默认调用父类无参的构造方法
- 如果子类构造方法中既没有显示标注,且父类中没有无参的构造方法,则编译出错
- 可以通过 super() 调用父类允许被访问的其他构造方法
- 使用 super 调用父类指定构造方法, 必须在子类的构造方法的第一行
代表父类引用
- 访问父类成员方法 super.print()
- 访问父类属性 super.name
- 访问父类构造方法 super()
10. super pk this
this: 当前类对象的引用
- 访问当前类的成员方法
- 访问当前类的成员属性
- 访问当前类的构造方法
- 不能在静态方法中使用
- 用于 调用构造方法时,必须放在有效代码第一行
super: 父类对象的引用
- 访问父类的成员方法
- 访问父类的成员属性
- 访问父类的构造方法
- 不能在静态方法中使用
- 使用 super 调用父类指定构造方法时, 必须在子类构造方法的第一行
- this() 和 super() 必须在子类的有效代码第一行,是不允许同时出现的.
四. java继承(下)
1. Object 类介绍
- Object 类是所有类的父类
- 一个类没有使用 extends 关键字明确标识继承关系,则默认继承 Object 类(包括数组)
- Java 中的每个类都可以使用 Object 中定义的方法
Object 中常见的2个方法如下:
equals 测试
- 继承 Object 中的 equals 方法时, 比较的是两个引用是否指向同一个对象
- 子类可以通过重写 equals 方法的形式,改变比较的内容
toString 测试
- 输出对象名时, 默认会直接调用类中的 toString 方法
- 继承 Object 中的 toString 方法时,输出对象的字符串表现形式: 类型信息+@+地址信息
- 子类可以通过重写 toString 方法的形式,改变输出的内容以及表现形式
2. final 关键字的使用
- 放在 类前: 表示 不允许被继承,如下
public final class Animal{}
或者
final public class Animal{}
- 放在 方法前: 表示 不允许被子类重写, 但是可以正常被子类继承使用.
public final void eat(){
System.out.println("吃东西方法");
}
- 放在 方法中的局部变量前: 表示 这个变量在具体被使用之前进行赋值即可,一旦赋值后续不允许被修改.
public void eat(String name){
final int temp = 10; //加了final表示 temp值 被第一次赋值之后就不允许被修改了.
}
- 放在 类中的成员属性前: 表示 赋值之后,不允许被修改. 赋值的过程: 1.定义时直接初始化 2.构造方法中赋值 3.构造代码块中赋值.
public class Animal{
public final int temp = 15;
public void eat(){
temp = 20; //报错,前面有 final, 后续不允许被修改.
}
}
注意: 放在 成员属性前,如果定义的时候没有赋值,那么 只能在 构造方法和构造代码块中赋值!!!
- 放在 引用类型前的时候, 实例化之后就不允许指向另外一个对象. 但是这个对象里面的属性值是可以修改的.
- final 不能放在构造方法前.
- 可以配合 static 一起使用. 表示全局不允许被修改的静态属性.
3. 注解介绍
- mac系统: option+/ 会弹出一个提示菜单 (win alt+/ )
@Override
public void eat(){
super.eat();
}
- @Override 注解
- 可以声明在包,类,属性,方法,局部变量,方法参数等的前面,用来对这些元素进行说明,注释.
按照运行机制分类
- 源码注解
- 编译时注解
- 运行时注解
按照来源分类
- 来自JDK的注解
- 来自第三方的注解
- 我们自己定义的注解
五. java单例模式
1. 设计模式概述
- 设计模式: 一套 反复使用的,多数人知晓的,经过分类编目的代码设计经验的总结.
- 简单翻译: 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.
创建型模式
- 工厂方法模式
- 创建者模式
- 抽象工厂模式
- 原型模式
- 单例模式
结构型模式
- 桥接模式
- 代理模式
- 享元模式
- 外观模式
- 装饰器模式
- 组合模式
- 适配器模式
行为型模式
-
备忘录模式
-
解释器模式
-
命名模式
-
中介者模式
-
观察者模式
-
策略模式
-
状态模式
-
模板方法模式
-
访问者模式
-
迭代子模式
-
责任链模式
-
设计模式是基于场景的解决方案
2. 单例模式的定义和作用
目的
- 使得类的一个对象成为该类系统中的唯一实例
定义
- 一个类有且仅有一个实例,并且自动实例化向整个系统提供
要点
- 某个类只能有一个实例
- 必须自动创建实例
- 必须自行向整个系统提供这个实例
实现
- 只提供私有的构造方法
- 含有一个该类的静态私有对象
- 提供一个静态的公有方法用来创建,获取静态私有对象
实现方案
- 饿汉式: 对象创建过程中实例化
- 懒汉式: 静态公有方法中实例化
3. 饿汉式的代码实现
- 饿汉式: 创建对象实例的时候直接初始化, 空间换时间
package com.imooc.singleton;
// 饿汉式: 创建对象实例的时候直接初始化, 空间换时间
public class SingletonOne {
// 1. 创建类中私有构造
private SingletonOne() {
}
// 2. 创建该类型的私有静态示例
private static SingletonOne instance = new SingletonOne();
// 3. 创建公有静态方法返回静态示例对象
public static SingletonOne getInstance() {
return instance;
}
}
4. 懒汉式的代码实现
- 懒汉式: 类内示例对象创建时并不直接初始化,直到第一次调用get方法时,才完成初始化操作.
package com.imooc.singleton;
// 懒汉式: 类内示例对象创建时并不直接初始化,直到第一次调用get方法时,才完成初始化操作.
// 时间换空间
public class SingletonTwo {
// 1. 创建私有构造方法
private SingletonTwo() {
}
// 2. 创建静态的该类示例对象
private static SingletonTwo instance = null;
// 3. 创建开放的静态方法提供实例对象
public static SingletonTwo getInstance() {
if (instance == null) {
instance = new SingletonTwo();
}
return instance;
}
}
5. 饿汉式 pk 懒汉式
- 饿汉式线程安全
- 懒汉式存在线程风险
懒汉式存在线程风险
- 同步锁
- 双重校验锁
- 静态内部类
- 枚举
6. 单例模式的特点及试用场景
单例模式优点
- 在内存中只有一个对象,节省内存空间
- 避免频繁的创建销毁对象,提高性能
- 避免对共享资源的多重占用
单例模式缺点
- 扩展比较困难
- 如果实例化后的对象长期不利用,系统将默认为垃圾进行回收,造成对象状态丢失
使用场景
- 创建对象时占用资源过多,但同时又需要用到该类对象
- 对系统内资源要求统一读写,如读写配置信息
- 当多个实例存在可能引起程序逻辑错误,如号码生成器
六. java多态
1. 课程简介
- 什么是多态?
- 多态在程序设计中的优势?
- 在Java中如何实现多态?
2. 多态的概念
-
多态: 意味着允许不同类的对象对同一个消息做出不同的响应.
-
编译时多态: 设计时多态,通过 方法重载 完成.
-
运行时多态: 程序运行时动态决定调用哪个方法. (大多数的多态都是运行时多态)
必要条件
- 满足继承关系
- 父类引用指向子类对象
3. 案例场景描述及实体类编写
4. 向上转型
- 向上转型,隐式转型,自动转型
- 父类引用指向子类实例,可以调用子类重写父类的方法 以及 父类派生的方法, 无法调动子类独有方法
- 注意: 父类中的静态方法无法被子类重写,所以向上转型之后,只能调用到父类原有的静态方法
- 小类 转为 大类
Animal one = new Animal(); // 1
Animal two = new Cat(); // 2
Animal three = new Dog(); // 3
5. 向下转型
- 向下转型(强制类型转换): 子类引用指向父类对象, 此处必须进行强转, 可以调动子类特有的方法
- 必须满足转型条件才能进行强转
Cat temp = (Cat) two;
temp.eat();
temp.run();
temp.getMonth();
6. instanceof 运算符
- instanceof运算符: 判断一个对象是否是一个类的实例 (true/false)
7. 类型转换总结
- 注意: 父类中的静态方法无法被子类重写,所以向上转型之后,只能调用到父类原有的静态方法
8. 类型转换案例
9. 抽象类
- java 中使用抽象类,限制实例化
- 抽象类: 不允许实例化,可以通过向上转型,指向子类实例
- abstract 和 public 的位置可以互换
public abstract class Animal{
}
应用场景
- 某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法.
10. 抽象方法
- 方法前加上 abstract, 而且方法不能有方法体.
- 字类必须 实现这个 抽象方法. 否则,子类也需要加上 abstract 变成抽象类.
public abstract void eat();
抽象类/抽象方法 使用规则
- abstract 定义抽象类
- 抽象类不能直接实例化,只能被继承,可以通过向上转型完成对象实例
- abstract 定义抽象方法,不需要具体的实现
- 包含抽象方法的类,一定是抽象类
- 抽象类中可以没用抽象方法
- static / final / private 不能与 abstract 并存
11. 问题引发的思考
- Java 中只支持单继承
- 引出问题: 如何解决一个类型中需要兼容多种类型特征的问题?
- 以及多个不同类型具有相同特征的问题呢?
12. 定义接口并测试
定义接口
package com.imooc.tel;
/**
* 具有照相能力的接口
*
* @author zhangqiang
*
*/
public interface IPhoto {
// 具有拍照的能力, public 可以省略
public void photo();
}
实现接口
- 关键字: implements
package com.imooc.tel;
public class Camera implements IPhoto {
@Override
public void photo() {
System.out.println("相机可以拍照");
}
}
如何调用
public class FourthPhone extends ThirdPhone implements IPhoto, INet {}
//==============
IPhoto ip = new FourthPhone();
ip.photo();
13. 接口成员–抽象方法&常量
-
接口定义了某一批类所需要遵守的规范
-
接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某些方法.
-
接口中抽象方法可以不写 abstract 关键字
-
访问修饰符默认 public ,可以不写
-
当类实现接口时,需要去实现接口中的所有抽象方法,否则需要将该类设置为抽象类
接口中 定义常量
package com.imooc.tel;
//接口访问修饰符: public 或者 不写为默认的.
public interface INet {
// public static final int TEMP = 20;
//接口中可以包含常量,默认 public static final
// 所以可以直接写
int TEMP = 20;
}
访问 接口中的常量
INet.TEMP 接口名.常量名
或者用如下访问:
INet net = new SmartWatch();
System.out.println(net.TEMP);
14. 接口成员–默认方法&静态方法
**jdk1.8之后,新增 默认方法. **
- 方法前 加上 default, 意思为默认方法, 这个方法可不强制实现.
package com.imooc.tel;
//接口访问修饰符: public 或者 不写为默认的.
public interface INet {
// default: 默认方法,可以带方法体 jdk1.8后新增
// 可以在实现类中重写,并可以通过接口的引用调用
default void connection() {
System.out.println("我是接口中的默认链接");
}
}
jdk1.8之后,新增 静态方法
package com.imooc.tel;
//接口访问修饰符: public 或者 不写为默认的.
public interface INet {
// static: 静态方法, 可以带方法体 jdk1.8后新增
// 不可以在实现类中重写,可以在接口名中调用
static void stop() {
System.out.println("我是接口中的静态方法");
}
}
- 调用静态方法
接口名.静态方法
接口中的默认方法,在实现类中是可以重写的
@Override
public void connection() {
// TODO Auto-generated method stub
INet.super.connection();
}
接口中的静态方法,在实现类中是无法重写的,可以用过接口名调用
15. 关于多接口中重名默认方法处理的解决方案
- 一个实现类中继承多接口,如果多接口中有重名方法,则需要自己重写这个方法!
- 一个实现类有父类,当父类和接口中有同名方法时,子类中优先调用父类中的这个方法! 这个和下面的变量不同.
16. 关于多重接口名常量处理的解决方案
- 当父类中的 成员属性和接口中的常量 同名的时候, 这时候,子类中是无法分辨的. 这个和上面的方法调用不同!
- 如上,解决方法是, 要在子类中定义个同名的变量, 当成类的 成员属性即可解决.
17. 接口的继承
- IFather,IFather2,ISon 都是接口,可以实现继承关系.
- 接口中的继承,可以继承多个父接口. 这个和类不同,类是单继承!
- 如果一个实现类继承这些接口,那么要实现接口和它的父接口中的所有方法!
package com.imooc.test;
// 接口也可以实现继承,并且可以继承多个父接口
public interface ISon extends IFather, IFather2 {
void run();
default void connection() {
System.out.println("ISon中的connection");
}
}
调用
package com.imooc.test;
public class Demo implements ISon {
@Override
public void say() {
// IFather 接口中定义的方法
}
@Override
public void run() {
// ISon 接口中定义的方法
}
@Override
public void fly() {
// IFather2 接口中定义的方法
}
}
接口可以继承多个接口,那么多个父接口中如果有同名的默认方法,该怎么处理?
- 在自己的接口中,重写这个方法,即可解决!
18. 内部类概述
- 内部类: 在 java 中,可以将一个类定义在另一个类里面或者一个方法里面, 这样的类称为内部类.
- 与之对应, 包含内部类的类被称为外部类
内部类的优势
- 内部类提供了更好的封装, 可以把内部类隐藏在外部类之内, 不允许同一个包中的其他类访问该类, 更好的实现了信息隐藏.
内部类的分类
- 成员内部类
- 静态内部类
- 方法内部类
- 匿名内部类
19. 成员内部类
- 内部类中最常见的就是成员内部类,也称为普通内部类
定义内部类
package com.imooc.people;
//外部类
public class Person {
int age;
public Heart getHeart() {
return new Heart();
}
// 成员内部类
class Heart {
public String beat() {
return "心脏在跳动";
}
}
}
调用内部类
- 方法1: new 外部类().new 内部类()
Person.Heart myHeart = new Person().new Heart();
- 外部类对象.new 内部类
Person.Heart myHeart1 = lili.new Heart();
- 外部类对象.获取方法()
Person.Heart myHeart2 = lili.getHeart();
package com.imooc.people;
public class PeopleTest {
public static void main(String[] args) {
Person lili = new Person();
lili.age = 12;
// 获取内部类对象实例:
// 方法1: new 外部类().new 内部类()
Person.Heart myHeart = new Person().new Heart();
System.out.println(myHeart.beat());
// 方法2: 外部类对象.new 内部类
Person.Heart myHeart1 = lili.new Heart();
System.out.println(myHeart1.beat());
// 方法3: 外部类对象.获取方法
Person.Heart myHeart2 = lili.getHeart();
System.out.println(myHeart2.beat());
}
}
成员内部类
- 内部类在外部使用时,无法直接实例化,需要借由外部类信息才能实现实例化.
- 内部类的访问修饰符,可以任意,但是访问范围会受到影响.
- 内部类可以直接使用外部类的成员信息(成员属性和成员方法)
- 如果内部类和外部类,有相同的成员属性,那么内部类中就优先调用内部类中的.
- 如果内部类和外部类,有相同的成员属性, 这时候内部类中优先调用的是内部类中定义的,但是如果想调用外部的,则可以用 外部类.this.成员的方式,访问外部类中同名的信息,例如 Person.this.age.
- 外部类访问内部类信息,需要通过内部类实例,无法直接访问
- 内部类编译后 .class 文件命名: 外部类$内部类.class
内部类中是否可以包含与外部类相同签名的方法?
- 内部类中可以有和外部类的同名方法,在内部类中调用的时候,会优先调用内部类中的方法.
- 如果在内部类有和外部类的同名方法, 要求使用外部类的, 要使用 外部类.this.成员方法()
20. 静态内部类
- static 修饰的内部类
- 静态内部类中,只能直接访问外部类的静态成员(成员属性和成员方法),如果需要调用非静态静态成员(成员属性和成员方法),可以通过对象实例.
package com.imooc.people;
//外部类
public class Person {
public int age;
public Heart getHeart() {
Heart one = new Heart();
one.temp = 12;
return new Heart();
}
public void eat() {
System.out.println("人会吃东西");
}
// 静态内部类
static class Heart {
int age = 1;
int temp = 22;
public static void say(){
System.out.println("hello");
}
public String beat() {
new Person().eat();
return new Person().age + "岁的心脏在跳动";
}
}
}
如何调用 静态内部类
- 静态内部类对象实例时,可以不依赖外部类对象
// 获取静态内部类对象实例
Person.Heart myHeart = new Person.Heart();
System.out.println(myHeart.beat());
- 可以通过 外部类.内部类静态成员 的方式,访问内部类中的静态成员
Person.Heart.say();
- 当内部类属性与外部类属性同名时, 默认直接调用内部类中的成员, 如果需要访问外部类中的静态属性,则可以通过 外部类.属性 的方式;如果需要访问外部类中的非静态属性, 则可以通过 new 外部类().属性 的方式.
21. 方法内部类
- 定义在外部类方法中的 内部类, 也成 局部内部类.
方法内部类
- 定义在方法内部,作用范围也在方法内
- 和方法内部成员使用规则一样,class前面不可以添加 public, private, protected, static.
- 类中不能包含静态成员(静态属性和静态方法)
- 类中可以包含 final, abstract修饰的成员
package com.imooc.people;
//外部类
public class Person {
public int age;
public Object getHeart() {
// 方法内部类
class Heart {
public int age = 1;
int temp = 22;
public void say() {
System.out.println("hello");
}
public String beat() {
new Person().eat();
return new Person().age + "岁的心脏在跳动";
}
}
return new Heart().beat(); //调用一个具体的方法
}
public void eat() {
System.out.println("人会吃东西");
}
}
如何调用 方法内部类
package com.imooc.people;
public class PeopleTest {
public static void main(String[] args) {
Person lili = new Person();
lili.age = 12;
System.out.println(lili.getHeart());
}
}
22. 匿名内部类
适用场景
- 只用到类的一个实例
- 类在定义后马上用到
- 给类命名并不会导致代码更容易被理解
匿名内部类
- 匿名内部类没有类型名称, 实例对象名称
- 编译后的文件命名: 外部类$数字.class
- 无法使用 private, public, protected, abstract, static
- 因为没有名字, 所以匿名内部类无法编写 构造方法. 可以添加构造代码块.
- 不能出现静态成员(属性和方法).
- 匿名内部类可以实现接口也可以继承父类,但是不可兼得
使用举例
- 定义父类
package com.imooc.anonymous;
public abstract class Person {
private String name;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void read();
}
- 匿名内部类的使用
package com.imooc.test;
import com.imooc.anonymous.Person;
public class PersonTest {
// 根据传入的不同的人的类型,调用对应的read方法
public void getRead(Person person) {
person.read();
}
public static void main(String[] args) {
PersonTest test = new PersonTest();
// 匿名内部类的使用
test.getRead(new Person() {
@Override
public void read() {
System.out.println("男生喜欢看科幻类书籍");
}
});
// 匿名内部类的使用
test.getRead(new Person() {
@Override
public void read() {
System.out.println("女生喜欢看言情小说");
}
});
}
}