B. 举例: Pig pp = (Pig)aa; //动物是猪
C. 应用: 多态的缺点是无法访问子类特有的成员,就需要使用向下转型,得到子类对象,再去调用特有成员
**注意事项**
在向下转型的过程当中,需要注意异常: ClassCastException 类型转换异常。
解释: 在向上转型的过程当中,原本是什么类型,转换的时候,就要变成什么类型,不要变成他的兄弟。
效果图
![在这里插入图片描述](https://img-blog.csdnimg.cn/1afe369bfe1e4767b6af0e21587a0f4a.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDk1MzE1Mg==,size_16,color_FFFFFF,t_70#pic_center)
如何解决问题?
解决类型转换异常的问题 `ClassCastException`
//判断当前的对象 是否来自于 类
if(对象名称 instanceof 类名称){
//向下转型的代码...
//调用子类特有成员的代码...
}
举例说明
//判断 对象ooo 他的类型是否是 来自于 Brother 类
if(ooo instanceof Brother){
//进行向下转型
Brother bbb = (Brother) ooo;
//调用子类特有的成员
System.out.println(bbb.ageZi);
bbb.methodBro();
}
#### [](
)第04节 指鹿为马
**案例代码**
动物接口
//动物接口
public interface Animal {
//吃
public abstract void eat();
}
马类
//马类
public class Ma implements Animal{
@Override
public void eat() {
System.out.println("马吃草...");
}
public void pao(){
System.out.println("马儿跑.");
}
}
鹿类
//鹿类
public class Lu implements Animal{
@Override
public void eat() {
System.out.println("鹿吃艹");
}
public void tiao(){
System.out.println("鹿儿跳.");
}
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
//创建对象
Ma make = new Ma();
use(make);
//直接调用方法(匿名对象的写法)
use(new Lu());
}
// 一个方法,既可以接收马的对象, 又可以接收鹿的对象
// 这个方法的参数应该怎么写呢?
// 这里就可以写 父类或者是父接口,采用多态传参
public static void use(Animal aa){ //Animal aa = new Ma();
//调用方法【共性方法父亲调用】
aa.eat();
//调用方法【如果想要访问特有的,应该向下转型】
//如果说 aa对象来自于 Ma类。 则向下转型成为Ma
if (aa instanceof Ma){
//向下转型
Ma make = (Ma) aa;
//调用子类特有方法
make.pao();
}
//再次判断 如果说 aa对象来自于 Lu类。
if (aa instanceof Lu){
//向下转型
Lu luhan = (Lu) aa;
//调用子类特有方法
luhan.tiao();
}
}
}
#### [](
)第05节 优点缺点
**优点**
-
提高代码的扩展性
解释: 如果说,我们将方法的参数定义为 父类或者是父接口。那么就可以传递 子类或者实现类。
以前的写法是 每一个子类都需要作为方法的参数,可能写很多的方法。
-
提高代码的复用性
解释:根据多态的前提条件,有父子关系。在继承的优点当中,就是提高代码的复用性。
我们将共性的代码交给父类。共性的方法交给父接口,这类写法,可以提高复用性。
父亲里面定义一份,多个儿子都可以使用。
**缺点**
无法访问子类 特有的成员(成员变量和成员方法)
如果想要访问,则需要判断和转型
[](
)第二章 内部类
--------------------------------------------------------------------------
#### [](
)第01节 基础理论
什么是内部类呢?
内部类就是说在一个类大括号里面,包含着另外一个类。
注意: 这里的包含关系,不是继承关系。(继承关系存在 is A 关系,子类可以看做父类)
生活实例:
身体和心脏的关系,就是一种包含关系。 在身体里面包含的有心脏。
内部类的分类有哪些?
(1)成员内部类。独立的分支:静态成员内部类
(2)局部内部类。独立的分支:匿名内部类
内部类的应用场景?什么时候会使用内部类
以前在学习 界面编程当中,有点击事件。(JavaScript 讲过点击的效果) 这里就会使用到内部类。
在 Android 编程当中使用较多。
有些 Java底层源代码当中,会使用到内部类。
#### [](
)第02节 成员内部类
**位置**
在一个类的成员变量或者成员方法的地方,编写类。(类当中,方法外)
**代码**
外部类和内部类
//身体(外部类)
public class Body {
String name = "身体";
private int age = 18;
public void exercise(){
System.out.println("身体成员方法...锻炼");
}
//-------------------
class Heart{
String name = "心脏";
public void jump(){
System.out.println("心脏跳...蹦蹦蹦..");
}
public void show(){
String name = "嘿嘿嘿";
System.out.println(name); //嘿嘿嘿
System.out.println(this.name); //心脏
System.out.println(Body.this.name); //身体
System.out.println(Body.this.age); //18
}
}
//-------------------
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
//如果想要创建,内部类的对象,应该怎么使用呢?
//公式是: 外.内
Body.Heart bh = new Body().new Heart();
//调用成员变量和成员方法
System.out.println(bh.name); //心脏
bh.jump(); //心脏跳...蹦蹦蹦..
System.out.println("--------");
bh.show();
}
}
#### [](
)第03节 静态成员内部类
**理论**
-
位置: 类中方法外的类。保证他是一个成员内部类
-
添加 static 关键字。
-
效果: 可以直接使用类名称打点调用
**代码**
外部类和内部类
//外部类
@SuppressWarnings(“all”)
public class Outer {
//静态变量
static String name = "外部类";
//内部类:静态内部类
static class Inner {
String name = "内部类";
public void inNoStatic() {
System.out.println("内部类...非静态方法");
}
public static void inYesStatic() {
System.out.println("内部类...静态方法");
}
}
}
测试类
public class Test {
public static void main(String[] args) {
//创建对象。访问非静态方法
Outer.Inner one = new Outer.Inner();
//调用方法
one.inNoStatic(); //内部类...非静态方法
//如果想要调用静态内部类当中的静态方法。
Outer.Inner.inYesStatic(); //内部类...静态方法
}
}
#### [](
)第04节 局部内部类
**理论**
什么是局部内部类啊?
-
位置:
定义在方法当中的类,叫做局部内部类。
-
范围:
只能在方法里面使用,出了方法,无法使用。
**代码**
外部类和内部类
//外部类:演示局部内部类
@SuppressWarnings(“all”)
public class Outer {
String name = "外部类";
//成员方法
public void show() {
//局部变量
int age = 18;
//打印输出
System.out.println(age);
}
//成员方法
public void method() {
//-------------------
class Inner {
String name = "内部类";
public void say() {
System.out.println("我是局部内部类成员方法");
}
}
//-------------------
//创建对象
Inner iii = new Inner();
iii.say();
}
}
测试类
//测试类
public class Test {
public static void main(String[] args) {
//只能创建外部类的对象
Outer ooo = new Outer();
//调用方法
ooo.method();
}
}
**面试问题**
![在这里插入图片描述](https://img-blog.csdnimg.cn/01b416d1d324436aacd55f84650ead00.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDk1MzE1Mg==,size_16,color_FFFFFF,t_70#pic_center)
#### [](
)第05节 匿名内部类
**理论**
-
介绍:
匿名内部类指的是没有名字的内部类,他是局部内部类的一种。
-
写法:
new 类名称/接口名称(参数){
//...方法...
};
**由来**
动物的接口
//动物的接口
@SuppressWarnings(“all”)
public interface Animal {
//吃的方法
public abstract void eat();
}
实现类
//实现类
@SuppressWarnings(“all”)
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃SHI");
}
}
测试类
//测试类
public class Test {
//思考问题:我们能不能省略 实现类(Dog) 不写呢?
public static void main(String[] args) {
//版本1:原始的用法,创建实现类的对象,去调用方法
Dog one = new Dog();
one.eat(); //狗吃SHI
System.out.println("---------");
//版本2: 多态的写法,左父右子
Animal two = new Dog();
two.eat(); //狗吃SHI
System.out.println("--------");
//版本3: 匿名内部类的写法
Animal three = new Animal() {
@Override
public void eat() {
System.out.println("狗吃SHISHI");
}
};
three.eat();
}
}
**应用场景**
如果方法的参数写的是接口,传递的是接口的实现类。(匿名内部类他也是接口的实现类)
拓展点:反编译的指令 `javap 类名称.class`
![在这里插入图片描述](https://img-blog.csdnimg.cn/9088ee140119469f98bf3d84f3f2e0dc.png#pic_center)
接口代码
//定义接口
public interface Usb {
//定义抽象方法: 连接
public abstract void connect();
}
测试类
public class Test {
public static void main(String[] args) {
//直接调用方法
useUsb(new Usb() {
@Override
public void connect() {
System.out.println("连接电脑");
}
});
}
//定义一个使用USB接口的方法
public static void useUsb(Usb sb){
//调用方法
sb.connect();
}
}
//小结:
//如果我们方法的参数写的是接口或者是类
//那么传递参数的时候,写的是实现类或者是子类。(也可以是匿名内部类)
//本质上面来说,匿名内部类就是子类或者实现类。 也就是多态的用法
//反编译操作: javap 类名称.class
[](
)第三章 内存回收
---------------------------------------------------------------------------
#### [](
)第01节 内存管理
内存划分图区域
![在这里插入图片描述](https://img-blog.csdnimg.cn/decd2caf03a34e2bae92447505f3ea88.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDk1MzE1Mg==,size_16,color_FFFFFF,t_70#pic_center)
**五个区域介绍**
1、程序计数器
-
较小
程序计数器是一块较小的内存空间,它的作用是当前线程执行指向字节码的行号指示器。
简单一点说,就是指挥程序执行那一条指令。(分支、循环、跳转、异常)
-
线程私有
Java虚拟机的执行,是由多条线程轮流切换处理器的时间来执行,
在任何一个时刻当中,一个处理器只会执行一条指令。
那么每个线程,都会存在一个独立的程序计数器,各个线程之间的程序计数器,不会受到影响的,独立存储。
-
无异常
如果线程正在执行的是一个 Java的方法,这个计数器记录的是正在执行的虚拟机字节码地址。
如果正在执行的是 Native 形式的方法,则计数器的值记录为空 Undefined
此时此刻内存区域是唯一一个在 Java规范当中没有任何的 OutOfMemoryError 情况区域。
2、虚拟机栈
-
线程私有
与程序计数器一样,Java虚拟机栈也是线程私有的,他的生命周期与线程相同。
-
描述Java方法执行的内存模型
每个方法被执行的时候,都会同时创建一个栈帧(Stack Frame 栈帧是方法运行时的基础数据结构)
用于存储局部变量表、操作栈、动态链接、方法出口灯信息。
每个方法被调用直到执行完毕的整个过程,都会对应一个栈帧在虚拟机栈,从入栈到出栈的过程。
-
异常
在Java虚拟机当中,规定了下面的两种异常情况。
【1】StackOverflowError JVM规定了栈的最大深度,如果执行方法超过最大深度,则出现栈溢出
【2】OutOfMemoryError JVM在扩展时候,无法申请到足够的内存,则出现内存溢出
3、本地方法栈
-
为 native 服务
虚拟机栈是为 Java服务的
本地方法栈是为 native 方法服务的(native方法底层是C\C++调用的是操作系统底层,例如声卡)
-
异常
与虚拟机栈一样,本地方法栈也会抛出 StackOverflowError 和 OutOfMemoryError
4、Java堆
-
最大
对于大多数应用来说,Java堆是 Java虚拟机管理的内存当中,最大的一块