面向对象的三大特征:封装继承多态
1、类和对象
类:抽象不具体的事物,通常表示一类事物的类别,在程序中表示模板
对象:具体的,真实存在的,可以看到的
1.1 如何创建类类中放所有对象共有的数据/属性--------变量
类中放所有对象共有的行为/方法--------方法
class Person{//人类的模板
//所有对象共有数据/属性
int age;//年龄
String name;//姓名
char sex;//性别
//所有对象共有的行为
void eat(){ //吃的行为
}
void sleep(){//睡觉
}
void play(){//玩耍
}
}
-----------------------------------------------
class car{//车类的模板
//共有数据
String color;//颜色
String type;//型号
double price;//价格
//共有的行为
void run(){//前进的行为
}
void back(){//后退的行为
}
void stop(){ //刹车的行为
}
}
-------------------------------------------------
class ObserverSubmarine{//侦察潜艇类
//所有侦察潜艇对象共有的数据
int x;
int y;
int width;//宽
int height;//高
int speed;//速度
//共有的行为
void step(){ //移动的行为
}
}
变量分为两种:
① 成员变量:声明在类的里面,方法外的变量叫做成员变量,作用域在整个类中
② 局部变量:声明在方法里面的变量,都叫做局部变量,作用域在当前方法中
package oo.day01;
public class Student {
//共有的属性 成员变量作用域在整个类体中
String name;//姓名
int age;//年龄
int stuID;//学号
//共有的行为
void study() {//学习的行为
//int a = 0;方法声明的变量为局部变量 作用域只在当前方法中可见
System.out.println(name + "在刻苦学习...");
}
void sayHi() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁了,我的学号是:" + stuID);
}
}
自定义的类也是一种数据类型
1.2 如何创建对象数据类型 变量名 创建一个学生对象 Student stu = new Student(); //创建了一个学生对象 将对象放到stu这个变量里面 Student zs = new Student(); //创建了一个学生对象 将对象放到zs这个变量里面 Student ls = new Student(); //创建了一个学生对象 将对象放到ls这个变量里面 1.3 如何访问对象
① 对象可以打点调出什么内容,取决一对象的模板中有什么。
② 那个对象 打点 调用属性 并赋值 那么就是那个对象的数据进行赋值。
③ 那个对象 打点 调用 方法 那么调用的方法里面用到的数据都是那个对象的 。
package oo.day01;
public class StudentDemo {
public static void main(String[] args) {
//创建对象的语法
Student zs = new Student();//创建一个学生对象 存到了 zs变量里面. 拿zs就相当于用这个具体对象!
//zs 这个变量 能打点调用 出什么 取决去当前 对象的模板!
//哪个对象 打点 调用 属性 并赋值 那么就是给那个对象存到数据
//访问对象的语法
zs.name = "张三"; //为zs这个对象的姓名赋值 为"张三"
zs.age = 30; //为zs这个对象的年龄赋值 为30
zs.stuID = 1001;//为zs这个对象的学号赋值 为1001
//哪个对象 打点 调用 方法 那么调用的方法里面用到数据都是那个对象的!
zs.study();//调用zs这个对象 的学习行为
zs.sayHi();//调用zs这个对象 的问好行为
//------------------------------------------
Student ls = new Student();//创建一个学生对象 赋值给 ls这个变量
ls.name = "李四";
ls.age = 40;
ls.stuID = 1002;
ls.study();
ls.sayHi();
}
}
1.4 构造方法
构造方法又称为构造器
① 适用性:在创建对象时,可以快熟的实现当前对象的属性赋值(初始化赋值)。
② 构造方法的语法:类名(){} (注意返回值那一说,加镂空就报错)
③ 作用:构造方法分本质作用就是写构造方法,系统会自动送一个无参数的构造方法。
Java规定:如果不给类模板写构造方法,系统会自动送一个无参的构造方法。
特性:当前类模板,若被创建对象时,系统则会自动调用相对应的构造方法。
④ 构造方法也可以重载:只需要满足构造方法名相同,参数个数或参数类型不同即可。
⑤ 如果在一个类中写了构造方法,系统则不会再赠送默认的构造方法(无参数的构造方法)
package oo.day01;
public class Student {
//共有的属性 成员变量作用域在整个类体中
String name;//姓名
int age;//年龄
int stuID;//学号
//有参数的构造方法
Student(String name1,int age1,int stuID1) { //构造方法再类被创建对象时执行
name = name1;
age = age1;
stuID = stuID1;
}
//构造方法语法: 类名(){}
//无参数的构造方法
Student(){
}
//共有的行为
void study() {//学习的行为
int a = 0;//方法声明的变量为局部变量 作用域只在当前方法中可见
System.out.println(name + "在刻苦学习..."); //隐式写法:this.name 代表当前对象的name
}
void sayHi() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁了,我的学号是:" + stuID);
}
}
1.5 this关键字
this代指的当前对象,使用this关键字可以区分成员变量 和 局部变量
哪个对选打点调用方法,那么this指代的就是这个对象
Java规定:局部变量可以于成员变量名字一样。在使用变量时,遵循就近原则
解决:可以用this关键字 明确表示 成员变量 避免于布局变量冲突的问题
main{
Student zs = new Student();
Student ls = new Student();
Student ww = new Student();
}
zs.sayHi();-----------------this 指代的就 zs 这个对象
ls.sayHi();-----------------this 指代的就 ls 这个对象
ww.sayHi();-----------------this 指代的就 ww 这个对象
默认值:引用类型的默认值都是null(String,数组,自定义类)
值类型中:整数默认值为0,小数默认为0.0 Boolean类型默认为FALSE
如果要拿一个null(空的)引用类型变量 打点访问内容,会报异常!
NullPointerException空指针异常
2、引用类型数组、继承的意义内存是JVM来进行分配划分:栈区、堆区、方法区。
基本数据类型的变量直接存数据,引用数据类型变量存的就会说方法中声明的变量
① 栈区:用来存放局部变量的区域,局部变量指的就是方法中声明的变量
特性:执行完以后,方法中所声明的变量 则会被销毁
② 堆区:用来存放对象的区域,对象指的是通过new关键字的语法创建的对象
特性:堆区的对象地址 若没有被引用的话,则会变为内存垃圾
③ 方法区:
GC(垃圾回收器):会不定时的区清理堆中存放的内存垃圾
数组的内存图:
① 基本数据类型数组
int[] array = new int[3];
② 引用数据类型数组
Student[] stus = new Student[3];2.2 继承
软件中的继承:继承代码,代码不用自己写,继承也能用
继承的适用性:当多个类之间存在一些共性属性和行为时,且他们在概念上达到一致,才可以额使用继承来优化冗余的代码
① 父类/超类:存放子类共有的属性和行为
② 子类/派生类:存放自己特有的属性和行为
③ 子类对象不仅可以访问自己的内容,还可以访问父类中的内容(继承过来就是自己有)
④ 父类对象 只能访问自己的内容
继承具有传递性,单一性(一个类中只能继承一个父类)
继承的语法:extends
泛化:从多个类中提取冗余代码到父类的过程 称为泛化!
class Person{ //人类 --------代表 父类 / 超类
String name; //姓名
int age;//年龄
char sex;//性别
//----------------------------
void eat(){ } //吃
void sleep(){ }//睡
}
class Student extends Person { //学生 子类 / 派生类
//学生对象共有的数据
int stuID;//学号
//共有的行为
void study(){ }//学习
}
class Teacher extends Person{ //老师类 子类 / 派生类
double salary;//工资
//行为
void teach(){ }
}
class Doctor extends Person{//医生 子类 / 派生类
int level;//职称
//
void cut(){ } //手术
}
继承的传递性:
class 爷爷类{
传家宝();
}
class 爸爸类 extends 爷爷类{
传家宝();
}
class 儿子类 extends 爸爸类{
传家宝();
}
Java规定:
实现子类之前,父类默认有自己的无参构造方法,构造方法不可以被继承,各自各的
① 如果实现了继承在创建子类对象时,子类构造方法回去执行父类的构造方法,然后再执行子类的构造方法内容,若没有明确父类或者子类提供构造方法,系统则会调用默认提供的构造方法。
子类 的构造方法中有隐式写法----->super();
② 若父类写了有参数的构造方法,而没有写无参的构造方法那么子类若实现继承,子类则报错!
需要手动明确的调用父类有参的构造方法
注意事项:若明确了子类的构造方法没吊用父类的构造方法时需要放在第一行。
现象:在创建子子对象时,一定会执行父类的构造方法,然后再执行子类的构造方法内容
package oo.day02;
public class SuperDemo {
public static void main(String[] args) {
Boo b = new Boo();//创建Boo这个对象
}
}
class Aoo { //父类
// Aoo() {
// System.out.println("父类Aoo的无参构造方法");
// }
Aoo(int a) {
System.out.println("a的值为:" + a);
}
}
class Boo extends Aoo {//子类
Boo() {//无参构造方法
super(5);
System.out.println("Boo的构造方法中的内容");
}
}
this:代表当前类
super: 代表父类
super.成员变量----------访问父类中的成员变量(应用率极低)
super.方法----------------访问父类的方法
super()-----------------访问父类无参构造方法,如果括号内写了参数,明确父类有参数的构造方法
继承中构造方法的强化:
学生类:
package oo.day02;
public class Student extends Person { //学生类继承了 人类
//学生对象共有的数据
int stuID;//学号
Student(String name,int age,char sex,int stuID){
super(name, age, sex);
this.stuID = stuID;
}
//共有的行为
void study(){//学习
}
}
//------------------------------------------------
package oo.day02;
public class Teacher extends Person {//老师类继承人类
double salary;//工资
Teacher(String name,int age,char sex,int salary){
super(name, age, sex);
this.salary = salary;
}
//行为
void teach(){ }
}
-------------------------------------------------
package oo.day02;
public class Doctor extends Person { //医生类继承 人类
int level;//职称
Doctor(String name,int age,char sex,int level){
super(name, age, sex);
this.level = level;
}
//
void cut(){ } //手术
}
-------------------Person类---------------------------
package oo.day02;
public class Person {
String name; //姓名
int age;//年龄
char sex;//性别
Person(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//----------------------------
void eat() {
} //吃
void sleep() {
}//睡
void sayHi(){
System.out.println("大家好,我叫"+name+"今年"+age+"性别是:"+sex);
}
}
问题:在测试代码中,因为有三个数组,学生数组,老师数组,医生数组,那么再去遍历当前这个数组中的信息时,需要写三个for循环
解决:把三个数组变成一个数组
2.3 向上造型① 声明父 new 子 的语法就叫做向上造型
② 变量打点调用什么,取决于这个变量类型有什么
③ 父大 子小
向上造型的好处:可以用父类型来代表不同的子类型
class Animal{ //动物类
}
class Tiger extends Animal { //老虎类
}
main{
类型 对象
Animal a = new Tiger(); //老虎 是 动物 ---语义通
Animal a1 = new Animal();//动物 是 动物 ---语义通
Tiger t = new Tiger(); //老虎 是 老虎 ---语义通
Triger t1 = new Animal();//动物 是 老虎 ---语义不通 代码会报错.
}
// 父 Person 子 学生 老师 医生
//声明父 new 子对象
Person p = new Student("小明",10,'男',1001);
//p能打点调用什么 取决于Person中有什么属性和行为
// p.stuID; p是Person 父类型,只能访问自己内容
Person p1 = new Teacher("小明",22,'男',122222);
Person p2 = new Doctor("小明",44,'男',5);
Person[] pp = new Person[3];//声明父类型数组
//父类型数组 可以存放不同的子类型对象.
pp[0] = new Student("小明",10,'男',1001);
pp[1] = new Teacher("小s",22,'男',122222);
pp[2] = new Doctor("小t",44,'男',5);
for (int i = 0; i < pp.length; i++) {
//调用父方法 执行子对象
// 编译期间 : 调用父类的sayHi功能
// 运行期间 : sayHi方法用的数据 取决于 当前对象是谁!
//pp[i].sayHi();
}
2.4 重写(override)
① 定义:子类可以通过重写的方式,来解决父类中存在的方法,子类不受用的清苦,重写的前提是发生在父子关系才可以
② 若子类重写了父类的方法,在编译期间调用的是父类的,运行后则执行的是子类重写后的那个方法。现象:调用父,执行子
③ 遵循两同两小原则,也可以重写
两同:方法名相同,参数列表相同
两小:子类在重写父类方法时,返回值类型要等于或小于父类中的那个方法
子类在重写父类的方法时,异常要等于或小于父类中的那个方法
一大:子类在重写父类方法时,访问权限要等于或者大于父类中的那个方法
重写的条件:子类若果想要重写父类中不适用的方法,首先在子类中写一个跟父类的那个方法一模一样的方法吗,方法体的内容,可以自行根据逻辑去实现即可
@Override
void sayHi() { //重写语法
System.out.println("我叫"+name+"我是一名学生,我的学号是"+stuID);
}
//-----------
@Override
void sayHi() {
System.out.println("我叫"+name+"我是一名老师,我的工资:"+salary);
}
------------
@Override
void sayHi() {
System.out.println("我叫"+name+"我是一名医生,我的职称:"+level);
}
重写强化:
重写的情况分类:
情况一、Boo子类只想吃西餐 ------------不需要重写
情况二、Boo子类只想喝果汁 ------------需要重写
情况三、Boo子类要吃西餐也要喝果汁 --------需要重写
class Aoo{//父类
void eat(){
System.out.println("吃西餐");
}
}
class Boo extends Aoo{ //子类
void eat(){
super.eat();//调用父类的eat方法 情况三
System.out.println("喝果汁");
}
}
重写和重载的区别:
重写(overide/overriding):发生在父子关系中,方法名和参数列表相同
重载(overload/overloading):发生在同一个类中(继承过来也算自己的),方法名相同,参数列表不同(个数或者类型)
class Aoo{ 父
void show(){
}
}
class Boo extends Aoo{ 子
void show(){ ----------发生了重写
}
}
-----------------------------------------
class Coo{
void show(){
}
}
class Doo extends Coo{
void show(int a){ -------------发生了重载
}
}
3、package和import 访问控制
2.1 package
如果仅仅是唯一的标识的话,很有可能类名发生冲突
1000个类想一千个类名,所以Java设计package
作用:避免类名的冲突
现象:同包中的类名是不能冲突的,不同包类名没有冲突
包名:包名纯纯小写
域名反写.项目名称.模块名.类名
2.2 import(导入)① 当前使用的类,在同一包小,可以直接访问,不需要导入
② 不同包的类,在使用的时候,需要通过import关键字进行导入(类的全名),才可以用
如果用的是Java所提供的功能,若导入功能的全包名,则不能使用
package oo.day02;
import oo.day01.Student;
import oo.day01.Test;//类的全包名
public class Demo {
public static void main(String[] args) {
//如果当前类要用到不同包下同名的文件时,需要明确写上类的全包名(这种情况少见)
oo.day01.Student s = new oo.day01.Student();
oo.day02.Student s2 = new oo.day02.Student("",1,'1',1);
}
}
2.3 访问修饰符
访问修饰符 可以修饰属性、方法、类(一般不管)
用来隐藏一些内容。用来暴露一些内容
属性封装:数据私化,方法公开化。为了数据安全,方法内可以去加一些限制条件,避免不合法的情况发生
① private:私有的,访问权限最小。只能当前类内部可见
② public:公开的,访问权限最大,其他位置都可以
③ 默认的:什么都不写,访问权限是本类和同包类都可以访问
④ protected:保护的,访问权限在本类中,同包子类。不同包的子类,不同的类不可以
一般在父子关系下,在父类中的数据和行为会加上protected
class Wife{ //老婆类
private String name = "王小花"; //属性私有化
private int age = 22; //属性私有化
private char sex = '女';
public void setAge(int age){ //通过方法公开化 可以让外部设置数据时 可以加一些要求
if(age < 25 && age >18){//
this.age = age;
}
}
public String getName(){ //方法公开化 让外部获取本类私有的name的方法
return name;
}
}
访问修饰符总结:
访问修饰符 类内部 同一包个中 非同包子类 非同包类 public √ √ √ √ protected √ √ √ 默认的 √ √ private √4、static和final关键字 4.1 final:最终
① 修饰变量:变量不能被二次修改 声明final变量时,需要初始化。
final修饰变量时 要写在变量类型的前方
② 修饰方法:用final修饰的方法,该方法是不允许被子类重写的!
final修饰方法时 要写在方法返回值的前方
③ 修饰类:用final修饰的类 该类就不能被继承
final修饰类时 要写在class前面
package oo.day03;
public class FinalDemo {
private int a;//声明int 的 成员变量 名为 a
// private final int c; 声明final的成员变量 需要声明时初始化
private final int b = 100;//变量 b 里面赋值的是 100
public void fun() {
System.out.println(b);
// b = 200; 编译错误:final修饰的变量不允许二次修改
final int d;//声明final的局部变量 声明时可以不做初始化
d = 150;//第一次赋值
// d = 350;编译错误:final修饰的变量不允许二次修改
}
}
//final class Aoo{ //父类 final 修饰的类不能被子类继承
// final void action(){ //final修饰的方法 不能被子类重写
// }
//}
//class Boo extends Aoo{ //子类
//
//}
4.2 static:静态
4.2.1 变量
①实例变量:属于对象的(有多少个对象就有多少分),在堆中存储,通过对象打点访问
②静态变量:属于累的(且只有一个),在方法区中存储,所有对象共享静态变量
修饰变量:用static修饰变量称之为静态变量,通过类名打点去访问
用static修饰时,写到变量的前方
.class字节码文件加载到方法区区中,且只会被加载一次
JVM:分配 栈区 堆区 方法区 1.栈区:存放局部变量 2.堆区:存放对象的区域 3.方法区:用来加载.class字节码文件(类中方法和静态方法/静态变量)
package oo.day03;
public class StaticDemo {
public static void main(String[] args) {
Coo c1 = new Coo();//创建Coo对象 存给了 c1
c1.show();//显示ab值的功能
Coo c2 = new Coo();//创建Coo对象 存给了 c2
c2.show();//显示ab值的功能
Coo c3 = new Coo();//创建Coo对象 存给了 c3
c3.show();//显示ab值的功能
//静态修饰变量 不需要创建对象进行访问 可以通过类名点的形式访问!
System.out.println(Coo.b);
Coo.b = 30;
}
}
class Coo {
int a;//实例变量 属于对象的
static int b = 10; //静态变量 属于类的
Coo() {
a++;
b++;
}
void show() { // 对象的a 类的 b
System.out.println("a的值为:" + this.a + ",b的值为:" + Coo.b);
}
}
静态适用性:
当有一份数据需要被某个类别下所有对象共享。
4.2.3 修饰方法①用static修饰的方法叫做静态方法,属于类,通过类名打点来使用
②存储在方法区中
③静态的方法中,没有this隐式的写法,所以也就会说说为什么静态的方法无法使用实例成员(实例的属性或者普通方法)
静态方法以后在写工具功能的时候,才会用到,且主要目的就是方便使用者通过类名点的形式就可可以访问。
class Coo {
int a;//实例变量 属于对象的
static int b = 10; //静态变量 属于类的
static void method(){ //静态方法
System.out.println(b);//隐式的写法 Coo.b
// System.out.println(this.a); 静态的方法是没有this传递的,所以无法访问this相关成员
// this.show(); 静态的方法是没有this传递的,所以无法访问this相关成员
}
Coo() {
a++;
b++;
//this.show();可以访问 有隐式的this 传递
}
void show() { // 对象的a 类的 b
System.out.println("a的值为:" + this.a + ",b的值为:" + Coo.b);
}
}
4.2.3 静态代码块
① 由static修饰的代码块,称之为静态代码块
② 属于类,当类被加载时(当第一次用到某个类时,类且只加载一次)静态代码则会被执行一次
③ 如果类被创建对象时,优先调用静态代码块,再调用该类的构造方法
④ 也可以直接通过访问类的静态成员 达到加载的方式
package oo.day03;
public class StaticDemo02 {
public static void main(String[] args) {
//类只会被加载一次
// Doo d1 = new Doo();//此时Doo类加载到方法区中
System.out.println(Doo.a);//这种情况也是可以使用类的加载
}
}
class Doo {
public static int a = 10; //静态变量 属于类的
Doo() {
System.out.println("Doo的无参构造方法执行了");
}
static { //静态代码块 当本类被加载到方法区时 执行 且只会执行一次
System.out.println("Doo的静态代码块执行了.....");
}
}
4.3 常量
用static final 修饰的变量称之为常量,应用率较高。
常量的特点:结合static和final的特性,通过类名访问,不能二次修改
常量命名是:纯大写。
何适用:;例如有一份数据,不会变化 且经常使用
package oo.day03;
public class StaticFinalDemo {
public static void main(String[] args) {
//编译期间自动会将常量中的内容 转换为具体的值 效率更高
// System.out.println(900);
System.out.println(Eoo.C);//通过类名点的形式访问常量
// Eoo.C = 1000; 不能二次修改
//1.假设第一次用Eoo,先将Eoo加载到方法区
//2.获取方法区中存的a这个静态的数据
System.out.println(Eoo.a);//通过类名点的形式访问静态变量
}
}
class Eoo{
static int a = 100;//静态变量 通过类名访问
final int b = 100;//fianl修饰的变量 不能二次修改 且需要声明时初始化
//常量
static final int C = 700;
}
4.4 抽象方法:
① 用abstract修饰的方法 为抽象方法。
用abstract修饰方法时,写到方法返回值前即可
② 抽象方法必须存在于抽象类中
③ 抽象方法不能有方法体
④ 抽象方法时需要*(必须)让子类重写实现的!
抽象类:
① 用 abstract修饰类 为抽象类
② 抽象类中是在原有的普通累的基础上可以多放些抽象方法(既可以放抽象也可以放普通方法)
③ 若一个类为抽象类,那么是需要有子类继承的
④ 子类必须重写抽象类中的所有抽象方法
⑤ 抽象类不能创建对象的,但是可以创建抽象类型数组的对象
⑥ 抽象类的意义:
封装共有属性和行为----代码复用
为子类提供向上造型----调父执行子
可以包含抽象方法,由具体抽象方法的具体实现逻辑
子类的行为逻辑虽然不同,但是我们在父类中写的抽象方法,在使用向上造型时,提供统一的入口
// SeaObject s = new SeaObject();//抽象类不能被创建对
SeaObject[] ss = new SeaObject[5];//创建抽象类型的数组对象可以
疑问:
1).抽象方法还有意义吗?为什么不直接用普通方法呢? 答:遵循面向对象设计规则,其次做成抽象方法的原因在于子类的行为逻辑都不一样,父类中做这个抽象方法就是为了约束子类必须要进行重写实现。 2).子类既然都重写了,那么父类的抽象方法为什么不能删? 答:父类中存在抽象方法 就是为了实现向上造型后,父类可以提供一个入口,统一管理子类的行为,具体执行时 执行的则是各个不同子类的实现逻辑。5、内部类 5.1 成员内部类(应用率不高)
类中套个类。外层的类称之为外部类,内层的类称之为内部类
① 内部类除了对外城 以外不具备可见性
② 内部类对象 可以在内部类创建
③ 内部类共享外部类的属性和行为(包括私有)
④ 内部类访问外部类 隐式写法 类名.this.xx
class Aoo{ //外部类
class Boo{ //内部类
}
}
-----------------
package oo.day04;
public class TestDemo {
public static void main(String[] args) {
Mama a = new Mama();
// Baby b = new Baby(); 内部类对外不具备可见性
}
}
class Mama{//外部类 --
private int a;
void action(){
Baby b = new Baby();//外部类可以创建内部类对象
}
class Baby{//内部类
int c;
void test(){
this.c = 100;//隐式this的传递 代表自己的东西
Mama.this.a = 10;//内部类可以共享外部类的属性和行为 隐式写法类名.this.xx
Mama.this.action();
}
}
}
5.2 匿名内部类(应用率高)
没有名字的内部类叫做匿名内部类
适用性:如果做一个子类,仅仅姿势想重写实现父类中某个方法时,其他地方也根部用不到这个类,那么我们可以直接使用匿名内部类的方式,快速实现重写的逻辑
① 匿名内部类只会存在于子类要重写父类的(抽象/普通)方法时,才会用到
② 匿名内部类 无法修改外部类的值 因为规定在匿名类的类体中使用外部的属性时,会自动修饰为final
package oo.day04;
public class NsInnerClassDemo {
public static void main(String[] args) {
int b = 10;
//使用匿名内部类的方式:
//1.创建Aoo的子类 只不过没有名字
//2.将当前这个没有子类对象 地址 赋值给了 a
//3.花括号就是子类的类体!
Aoo a = new Aoo() { //创建的匿名内部类的外部类是NsInnerClassDemo这个类
//创建的匿名内部类的父类是 Aoo这个类
@Override
public void show() {
// b = 100; 匿名内部类无法修改外部类的属性的
// System.out.println(b); 匿名内部类使用外部类属性会默认为 final
System.out.println("匿名内部类重写Aoo的show方法");
}
};
a.show();
//常规的重写
// Boo b = new Boo();
// b.show();
}
}
class Aoo { //-----作为父类
public void show() {
System.out.println("Aoo这个父类的show方法");
}
}
//常规的重写
class Boo extends Aoo { //构建父子关系
@Override
public void show() {
System.out.println("Boo子类重写父类的show方法");
}
}
面试题
问:内部类有独立的.class字节码文件吗? 有! 成员内部类: 外部类的类名 字节码文件 成员内部类 内部类的字节码文件 Mama Mama.class Mama.Baby Mama$Baby.class 内名内部类: 外部类的类名 字节码文件 匿名内部类的字节码文件 NsInnerClassDemo NsInnerClassDemo.class NsInnerClassDemo$1.class5.3 向上造型、自动类型转换
① 父大子小------声明父new子
② 引用类型变量,能打点调出什么,取决于当前变量的类型
③ 能向上成功的:父new子/接口 new 实现类
向下转型/强制类型转换,可以成功的两个条件,满足其一即可:
条件一: 要强转的引用类型变量中存在引用 就是转换的这个类型
条件二:要强转的引用类型变量中存在引用 实现了转换的接口类型,或继承了该类
我们可以通过instanceof关键字来判断,判断当前队形是否符合要强转的类型
Aoo o = new Boo();//向上造型
Boo o1 = (Boo)o;//可以完成强制转换 :符合强转成功的条件一
Inter1 i1 = (Inter1)o;//可以完成强制转换: 符合强转成功的条件二
Coo c1 = (Coo)o;//强制转换失败的异常:classcastException
interface Inter1{
}
class Aoo{//父类
}
class Boo extends Aoo implements Inter1{
}
class Coo extends Aoo{
}
package oo.day05;
public class ClassCastDemo {
public static void main(String[] args) {
Aoo o = new Boo();//声明父new子 o中存的引用是Boo这个类型对象。
Boo o1 = (Boo)o;//符合条件一:要强转的变量引用 就是这个类型
if(o instanceof InterA){ //判断o对象是否可以转换为 InterA 接口类型
System.out.println("o可以强转InterA接口类型");
InterA i1 = (InterA)o;//符合条件二:要强转的变量引用 实现了该接口类型 或继承了该类
}
if(o instanceof Coo){//判断o对象是否可以转换为 Coo类型
System.out.println("o是Coo这个类型");
Coo c1 = (Coo)o;
}
Coo c1 = (Coo)o;//ClassCastException类型转换异常
// Aoo o = new Boo();
// Coo c1 = (Coo)o;
}
}
interface InterA{
}
class Aoo{
}
class Boo extends Aoo implements InterA{
}
class Coo extends Aoo{
}



