栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java基础--面向对象(上)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java基础--面向对象(上)

面向对象的三大特征:封装继承多态

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.class
5.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{
​
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/874247.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号