# 面向对象oop

#

# 什么是类?什么是对象?

  • 现实世界是由很多对象组成的
    基于对象抽出了类
  • 对象:真实存在的单个个体
    类:类型/类别,代表一类个体
  • 类中可以包含:
    1. 所有对象所共有的属性/特征----------------成员变量
    2. 所有对象所共有的行为---------------------方法
  • 一个类可以创建多个对象
    同一类型所创建出来的对象,结构相同,数据不同
  • 类是对象的模板,对象是类具体的实例

# 如何创建类?如何创建对象?如何访问成员?

//学生类  
public class Student {  
    //成员变量  
    String name;    
    int age;  
    String address;  
    //方法  
    void study(){  
        System.out.println(name+"在学习...");  
    }  
    void sayHi(){  
        System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);  
    }  
}  
package oo.day01;  
//学生类的测试类  
public class StudentTest {  
    public static void main(String[] args) {  
        //创建学生对象  
        Student zs = new Student();  
        //给成员变量赋值  
        zs.name = "zhangsan";  
        zs.age = 25;  
        zs.address = "河北廊坊";  
        //调用方法  
        zs.study();  
        zs.sayHi();  
          
        Student ls = new Student();  
        ls.name = "lisi";  
        ls.age = 27;  
        ls.address = "黑龙江佳木斯";  
        ls.study();  
        ls.sayHi();     
        //new后所有成员变量都有默认值  
        Student ww = new Student();  
        ww.study();  
        ww.sayHi(); 
    }  
}
  • shootGame流程(先找对象,基于对象抽类)
    1. 先找对象:英雄机、天空、小敌机、大敌机、小蜜蜂、子弹
    2. 抽类:Hero、Sky、Airplane、BigAirplane、Bee、Bullet
    3. 设计类中的成员变量和方法
    4. 创建对象并测试
    类(数据类型)   引用类型变量       指向     对象
    Student            zs          =      new Student();
    
    基本类型变量(变量)
    Int   a ;
    
  • 补充:
    1. 类型----------默认值null
    2. 面向过程的结构化程序设计弊端:java是面向对象的
    • 缺乏对数据的封装
    • 数据与方法(操作数据)的分离
    1. 高质量的代码:
    • 复用性好、扩展性好、维护性好、
    • 可移植性好、健壮性好、可读性好、效率好...

# 方法

  • 方法的签名:方法名+参数列表

# 方法的重载(Overload)

  1. 发生在一个类中,方法名称相同,参数列表不同
  2. 编译器在编译时会根据方法的签名自动绑定调用的方法
/* 
 * 补充: 
 * 1)一个文件中是可以创建多个类的 
 * 2)public修饰的类只能有一个 
 * 3)public修饰的类必须与文件名相同 
 */  
//重载的演示  
public class OverloadDemo {  
    public static void main(String[] args) {  
        Aoo o = new Aoo();  
        o.say();  
        o.say("zhangsan");  
        o.say(25);  
        o.say("zhangsan", 25);  
        o.say(25, "zhangsan");  
    }  
}  
  
class Aoo{  
    void say(){}  
    void say(String name){}  
    void say(int age){}  
    void say(String name,int age){}  
    void say(int age,String name){}  
      
    //int say(){return 1;} //编译错误,重载与返回值类型无关  
    //void say(String address){} //编译错误,重载与参数名称无关  
}  

# 构造方法

  • 常常给成员变量赋初值
  • 与类同名,没有返回值类型
  • 在创建(new)对象时被自动调用

    new Student() 调用了Student的构造方法

  • 若自己不写构造方法,则编译器默认一个无参构造方法,

    TIP

    若自己写了构造方法,则不再默认提供

  • 构造方法可以重载

# this

  • 指代当前对象,哪个对象调用方法指的就是哪个对象
  • 只能用在方法中,方法中访问成员变量之前默认有个this.
  • this的用法:
    1. this.成员变量名-----------------访问成员变量 成员变量和局部变量可以同名,但是要用this关键字
    2. this.方法名()-------------------调用方法(一般不用)
    3. this()--------------------------调用构造方法
public class Student {  
    String name;    
    int age;  
    String address;  
    Student(String name){  
        this(name,0,null);  //调构造方法
    }  
    Student(String name,int age){  
        this(name,age,null);  
    }  
    Student(String name,int age,String address){  
        this.name = name;  
        this.age = age;  
        this.address = address;  
    }  
    void study(){  
        System.out.println(name+"在学习...");  
    }  
    void sayHi(){  
        System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);  
    }  
}  

# null

  • 空,没有指向任何对象
  • 若引用的值为null,则该引用不能再进行任何操作了,
    • 若操作则发生NullPointerException空指针异常

# 类型画等号

# 引用类型变量之间画等号

  • 指向同一个对象
  • 通过一个引用对数据的修改会影响另一个引用对数据的访问

    eg: 房子钥匙

# 基本类型变量之间画等号

  • 赋值
  • 对一个变量的修改不会影响另一个变量的值

    eg: 身份证复印件

public class NullRefDemo {  
    public static void main(String[] args) {  
        Student zs = new Student("zhangsan",25,"LF");  
        System.out.println(zs.age); //25  
        Student zss = zs; //指向同一个对象  
        zss.age = 28;  
        System.out.println(zs.age); //28  
          
        int a = 5;  
        int b = a; //赋值  
        a = 8;  
        System.out.println(b); //5  
  
        Student stu = new Student("zhangsan");  
        System.out.println(stu.name);  
        stu = null; //空,没有指向任何对象  
        System.out.println(stu.name); //空指针异常       
    }  
} 

# 引用类型数组

1)Student[] stus = new Student[3];
  stus[0] = new Student("zhangsan",25,"LF");
  stus[1] = new Student("lisi",26,"JMS");
  stus[2] = new Student("wangwu",27,"SD");
  stus[1].age = 36; //给stus中第2个元素的年龄赋值为36
2)Student[] stus = new Student[]{
    new Student("zhangsan",25,"LF"),
    new Student("lisi",26,"JMS"),
    new Student("wangwu",27,"SD")
};
3)int[][] arr = new int[3][];----------数组的数组
  arr[0] = new int[2];
  arr[1] = new int[3];
  arr[2] = new int[2];
  arr[1][0] = 100; //给arr中第2个元素中的第1个元素赋值为100
4)int[][] arr = new int[3][4];
  //3:为as的长度
  //4:为as中每个元素的长度
  for(int i=0;i<arr.length;i++){
    for(int j=0;j<arr[i].length;j++){
      arr[i][j] = 100;
    }
  }

# 继承

# 继承描述

  • 作用:代码复用
  • 通过extends来实现继承
  • 超类:所有派生类所共有的属性和行为
    派生类:派生类所特有的属性和行为
  • 派生类继承超类后,派生类具有:超类的+派生类的
  • 一个超类可以有多个派生类
    一个派生类只能有一个超类-------单一继承
  • 继承具有传递性
  • java规定:构造派生类之前必须先构造超类

TIP

在派生类的构造方法中若没有调用超类的构造方法 ----------则默认super()调用超类的无参构造方法
在派生类的构造方法中若调用了超类的构造方法 ----------则不再默认提供

- super()调用超类构造方法,必须位于派生类构造的第1句

# super

指代当前对象的超类对象

  • super的用法:
    1. super.成员变量名-----------------访问超类的成员变量
    2. super.方法名()-------------------调用超类的方法
    3. super()--------------------------调用超类的构造方法
//super的演示(含继承)  	
public class SuperDemo {  
    public static void main(String[] args) {  
        Boo o = new Boo();  
    }  
}  
  
class Coo{  
    Coo(int a){  
    }  
}  
  
class Doo extends Coo{  
    Doo(){  
        super(5);  
    }  
    /* 
    //如下代码,写不写都有 
    Doo(){ 
        super(); 
    } 
    */  
}  
  
class Aoo{  
    Aoo(){  
        System.out.println("超类的构造方法");  
    }  
}  
class Boo extends Aoo{  
    Boo(){  
        //super(); //默认的(调用超类的构造方法)  
        System.out.println("派生类的构造方法");  
    }  
} 

# 向上造型

  • 超类型的引用指向派生类对象
  • 能点出来什么,看引用的类型
//向上造型的演示  
public class UpLoadDemo {  
    public static void main(String[] args) {  
        Aoo o1 = new Aoo();  
        o1.a = 1;  
        o1.show();  
        //o1.b = 2; //编译错误,超类不能访问派生类的  
          
        Boo o2 = new Boo();  
        o2.b = 1;  
        o2.test();  
        o2.a = 2;  //正确,派生类可以访问超类的  
        o2.show();  
          
        Aoo o3 = new Boo(); //向上造型  
        o3.a = 1;  
        o3.show();  
        //o3.b = 2; //编译错误,能点出来什么,看引用的类型  
    }  
}  
  
class Aoo{  
    int a;  
    void show(){}  
}  
class Boo extends Aoo{  
    int b;  
    void test(){}  
}

# 方法的重写(Override)

重新写、覆盖

  • 发生在父子类中,方法名称相同,参数列表相同,方法体不同
  • 重写方法被调用时,看对象的类型
  • 重写遵循"两同两小一大"原则:
    1. 两同:
      • 方法名相同
        • 参数列表相同
    2. 两小:
    - 派生类方法的返回值类型小于或等于超类方法的
        - void时,必须相等
    	  - 基本类型时,必须相等
    	  - 引用类型时,小于或等于
      - 派生类方法抛出的异常小于或等于超类方法的
    
    1. 一大:
    - 派生类方法的访问权限大于或等于超类方法的
    
//超类大,派生类小  
class Eoo{  
    void show(){}  
    double test(){return 0.0;}  
    Foo sayHi(){return null;}  
    Eoo say(){return null;}  
}  
class Foo extends Eoo{  
    //int show(){return 1;} //编译错误,void时必须相等  
    //int test(){return 0;} //编译错误,基本类型时必须相等  
    //Eoo sayHi(){return null;} //编译错误,引用类型时必须小于或等于  
    Foo say(){return null;}  
} 
//测试类,含向上造型与重写  
public class Test {  
    public static void main(String[] args) {  
          
        Person[] ps = new Person[5];  
        ps[0] = new Student("zhangsan",25,"LF","111");  
        ps[1] = new Student("lisi",22,"JMS","222");  
        ps[2] = new Teacher("aaa",37,"LF",5000);  
        ps[3] = new Teacher("bbb",45,"LF",8000);  
        ps[4] = new Doctor("ddd",35,"LF","主治医师");  
        for(int i=0;i<ps.length;i++){  
            System.out.println(ps[i].name);  
            ps[i].sayHi();  
        }  
          
          
        /* 
        Student zs = new Student("zhangsan",25,"LF","111"); 
        Teacher ls = new Teacher("lisi",37,"LF",5000); 
        Doctor ww = new Doctor("wangwu",35,"LF","主治医师"); 
        zs.sayHi(); 
        ls.sayHi(); 
        ww.sayHi(); 
        */  
        /* 
        Student zs = new Student("zhangsan",25,"LF","111"); 
        //Person zss = new Student("zhangsansan",25,"LF","111"); 
        zs.sayHi(); 
        //zss.sayHi(); //重写方法被调用时,看对象的类型 
        */  
    }  
}  
class Person{  
    String name;  
    int age;  
    String address;  
    Person(String name,int age,String address){  
        this.name = name;  
        this.age = age;  
        this.address = address;  
    }  
    void sayHi(){  
        System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);  
    }  
}  
class Student extends Person{  
    String stuId;  
    Student(String name,int age,String address,String stuId){  
        super(name,age,address);  
        this.stuId = stuId;  
    }  
    void sayHi(){  
        System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address+",学号为:"+stuId);  
    }  
}  
class Teacher extends Person{  
    double salary;  
    Teacher(String name,int age,String address,double salary){  
        super(name,age,address);  
        this.salary = salary;  
    }  
    void sayHi(){  
        System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address+",工资为:"+salary);  
    }  
}  
class Doctor extends Person{  
    String level;  
    Doctor(String name,int age,String address,String level){  
        super(name,age,address);  
        this.level = level;  
    }  
}  

# 重写与重载的区别

TIP

常见面试题

  • 重写:
    1. 发生在父子类中,方法名称相同,参数列表相同,方法体不同
    2. 遵循"运行期绑定",看对象的类型来调用方法
  • 重载:
    1. 发生在一个类中,方法名称相同,参数列表不同,方法体不同
    2. 遵循"编译期绑定",看引用/参数的类型来绑定方法

TIP

编译期:.java源文件,经过编译,生成.class字节码文件--检测语法
运行期:JVM加载.class并运行.class

Class Aoo{
void show(){}
}
Class Boo extends Aoo{
void show(String name){}//重载
}  
package oo.day04;  
//重写与重载的演示  
public class OverrideOverloadDemo {  
    public static void main(String[] args) {  
        Goo goo = new Goo();  
        Coo o = new Doo();  
        goo.test(o); //重载看参数/引用(Coo)  
    }  
}  
class Goo{  
    void test(Coo o){  
        System.out.println("超类型参数");  
        o.show(); //重写看对象(Doo)  
    }  
    void test(Doo o){  
        System.out.println("派生类型参数");  
        o.show();  
    }  
}  
class Coo{  
    void show(){  
        System.out.println("超类show");  
    }  
}  
class Doo extends Coo{  
    void show(){  
        System.out.println("派生类show");  
    }  
} 
补充:
class World{
  FlyingObject[] enemies;
  FlyingObject[] enemies = null; 什么也没有,空

  FlyingObject[] enemies = {};   有,但是没放东西
  FlyingObject[] enemies = new FlyingObject[0];  
}

#

# package

  1. 作用:避免类名冲突
  2. 包名可以有层次结构
  3. 类的全称为: 包名.类名
  4. 建议:包名所有字母都小写

# import

  1. 同包中的类可以直接访问,
    不同包中的类不能直接访问,想访问只有如下两种方式:
  2. 先import声明类再使用类----建议
  3. 类的全称------------------太繁琐,不建议
import引进的一串英文的含义
  建议:
域名反写   . 项目名称 . 模块名称      . 类名
cn.tedu    . aproject . stumanager      .
cn.tedu    . aproject . teachermanager  .

# 访问控制修饰符

  • public:公开的,任何类
  • private:私有的,本类
  • protected:受保护的,本类、子类、同包类
  • 默认的:什么也不写,本类、同包类

    TIP

    1. 类的访问修饰只能是public和默认的
    2. 类中成员的访问修饰如上四种都可以

WARNING

数据(变量)私有化(private),行为(方法)公开化(public)

# final

最终的、不可改变的---------------应用率低

  • 修饰变量:变量不能被改变
  • 修饰方法:方法不能被重写
  • 修饰类:类不能被继承,但是可以继承别的类
//final的演示  
public class FinalDemo {  
    public static void main(String[] args) {  
    }  
}  
  
/* 
 * final修饰成员变量,只有如下两种初始化方式: 
 *   1)声明的同时初始化 
 *   2)在构造方法中初始化 
 * final修饰局部变量,在使用之前赋值即可 
 */  
class Eoo{ //演示final修饰变量  
    final int a = 5;  
    final int b;  
    Eoo(){  
        b = 6;  
    }  
    void test(){  
        final int c;  
        //a = 55; //编译错误,final的变量不能被改变  
    }  
}  
  
//演示final修饰方法  
class Foo{  
    final void show(){}  
    void test(){}  
}  
class Goo extends Foo{  
    //void show(){} //编译错误,final的方法不能被重写  
    void test(){}  
}  
  
//演示final修饰类  
final class Hoo{}  
//class Ioo extends Hoo{} //编译错误,final修饰的类不能被继承  
class Joo{}  
final class Koo extends Joo{}  

# static

静态的

  • 静态变量:(static的称为实例变量)
    1. 由static修饰
    2. 属于类,存储在方法区(方法区只被加载一次)中,只有一份
    3. 常常通过类名点来访问
    4. 何时用:所有对象所共享的数据(图片、音频、视频等)

WARNING

注:结合演示静态变量代码来理解,只有一份

  • 静态方法:

    1. 由static修饰
    2. 属于类,存储在方法区中,只有一份
    3. 常常通过类名点来访问
    4. 静态方法中没有隐式的this传递,
      静态方法中不能直接访问实例成员
    5. 何时用:方法的操作仅与参数相关而与对象无关
  • 静态块:

    1. 属于类,在类被加载期间自动执行,
      因类只加载一次,所以静态块也只执行一次
    2. 何时用:加载/初始化静态资源(图片、音频、视频等)(如果直接初始化而不是用静态块)
//static的演示  
public class StaticDemo {  
    public static void main(String[] args) {  
        Loo o1 = new Loo();  
        o1.show(); //1,1  
        Loo o2 = new Loo();  
        o2.show(); //1,2  
        System.out.println(Loo.b); //2  
        //System.out.println(o1.b); //不建议通过对象名来访问  
          
        Moo.test();  
          
        Noo o3 = new Noo();  
        Noo o4 = new Noo();   
    }  
}  
  
class Loo{ //演示静态变量  
    int a;  
    static int b;  
    Loo(){  
        a++;  
        b++;  
    }  
    void show(){  
        System.out.println("a="+a+",b="+b);  
    }  
}  
  
class Moo{ //演示静态方法  
    int a;  
    static int b;  
    void show(){ //有this  
        System.out.println(this.a);  
        System.out.println(Moo.b);  有一个隐式的类名点
    }  
    static void test(){ //没有this  
        //静态方法没有隐式的this传递  
        //没有this就意味着没有对象  
        //而实例成员a必须通过对象点来访问  
        //System.out.println(a); //编译错误  在a	前加this.就可以
        System.out.println(Moo.b); 有一个隐式的类名点
    }  
}    
  
class Noo{ //演示静态块  
    static{  
        System.out.println("静态块");  
    }  
    Noo(){  
        System.out.println("构造方法");  
    }  
}  
  • static final常量:(一般用public修饰 (是建议))
    1. 必须声明同时初始化
    2. 通过类名点来访问,不能被改变
    3. 建议:常量名所有字母都大写,多个单词用_分隔
    4. 编译器在编译时将常量直接替换为具体的值,效率高
    5. 何时用:数据永远不变,并且经常使用
//static final的演示  
public class StaticFinalDemo {  
    public static void main(String[] args) {  
        System.out.println(Aoo.PI);  
        //Aoo.PI = 3.1415926; //编译错误,常量不能被改变  
          
        //1)加载Boo.class到方法区中  
        //2)num一并存储在方法区中  
        //3)到方法区中获取num的值并输出  
        System.out.println(Boo.num);  
          
        //编译器在编译时将常量直接替换为具体的值,效率高  
        //相当于System.out.println(5);  
        System.out.println(Boo.COUNT);  
          
    }  
}  
  
class Boo{  
    public static int num = 5; //静态变量  
    public static final int COUNT = 5; //常量  
}  
  
  
class Aoo{  
    public static final double PI = 3.1415926;  
//public static final int NUM; //编译错误,常量必须声明同时初始化 
} 

# abstract

  • 抽象方法:
    1. 由abstract修饰
    2. 只有方法的定义,没有具体的实现(连{}都没有)
  • 抽象类:
    1. 由abstract修饰
    2. 包含抽象方法的类必须是抽象类
      不包含抽象方法的类也可以声明为抽象类-------我乐意(无意义)
    3. 抽象类不能被实例化(因为抽象方法不完整,所以抽象类也不完整,不能被实例化,也 就式不能被new对象)
    4. 抽象类都是需要被继承的,派生类:
    • 重写抽象类中的所有抽象方法-----变不完整为完整
      • 也声明为抽象类-----------------一般不这么做
    1. 抽象类的意义:
    • 封装派生类所共有的属性和行为-----------代码复用
      • 为所有派生类提供统一的类型-------------向上造型(向上造型能点出来)
    • 可以包含抽象方法,为所有派生类提供统一的入口
      • 派生类的具体实现不同,但入口是一致的
    补充:
    int index = 0; //活着的下标  
        /** 重写getiImage()获取图片*/  
        public BufferedImage getImage(){ //每10毫秒走一次  
            if(isLife()){//若活着  
                return images[index++%2];  //利用这个【index++%2】可以使0,1,											  0,1替换,想几个数切换就取余几
            }  
            return null;  
    }  
    

# 设计规则

  • 将所有派生类所共有的属性和行为,抽到超类中-------抽共性
  • 所有派生类的行为都一样,设计为普通方法
  • 所有派生类的行为都不一样,设计为抽象方法

# 成员内部类

  • 类中套类,里面的称为Inner内部类,外面的称为Outer外部类
  • 内部类通常只服务于外部类,对外不具备可见性
  • 内部类通常是在外部类中创建的
  • 内部类中可以直接访问外部类的成员(包括私有的)
    内部类中有一个隐式的引用指向了创建它的外部类对象

    eg: 外部类名.this.

public class InnerClassDemo {  
    public static void main(String[] args) {  
        Mama m = new Mama();  
        //Baby b = new Baby(); //编译错误,内部类对外不可见  
    }  
}  
  
class Mama{  
    private String name;  
    void createBaby(){  
        Baby b = new Baby(); //内部类对象通常是在外部类中创建的  
    }  
    class Baby{  
        void showMamaName(){  
            System.out.println(name);  
            System.out.println(Mama.this.name);  
            //System.out.println(this.name); //编译错误  
        }  
    }  
}

# 匿名内部类

  • 若想创建一个类的对象,并且对象只被创建一次,
    此时该类不必命名,称之为匿名内部类
  • jdk1.7(含)版本之前,匿名内部类中想访问外面的变量,(切记) 要求该变量必须是final的
public class NstInnerClassDemo {  
    public static void main(String[] args) {  
        //1)创建了Coo的一个派生类,但是没有名字  
        //2)为该派生类创建了一个对象,名为o1  
        //3)大括号中的为派生类的类体  
        Coo o1 = new Coo(){  
              
        };  
          
        //1)创建了Coo的一个派生类,但是没有名字  
        //2)为该派生类创建了一个对象,名为o2  
        //3)大括号中的为派生类的类体  
        Coo o2 = new Coo(){  
              
        };  
          
        final int num = 5;  
        Doo o3 = new Doo(){  
            void show(){  
                System.out.println("showshow");  
                System.out.println(num); //1.7(含)以前的版本要求num必须是												final的  
            }  
        };  
        o3.show();  
          
    }  
}  
  
abstract class Coo{  
}  
  
abstract class Doo{  
    abstract void show();  
} 

# 补充

  • 先写行为(方法):
    1. 若是某个对象特有的行为,则将行为设计在派生类中
      若是所有对象共有的行为,则将行为设计在超类中
      • 所有派生类行为都一样,设计为普通方法
    • 所有派生类行为都不一样,设计为抽象方法,而后派生类重写
    1. 将部分派生类所共有的行为,设计在接口中
      符合既是也是原则时,用接口实现
      接口是对继承的单根性的扩展----实现多继承
  • 窗口调用:
    1. 定时触发的,在run()中调用
    2. 事件触发的,在侦听器中调用
    3. 画出来的,在paint()中调用

# 接口

  • 是一种数据类型(引用类型)
  • 由interface定义的
  • 只能包含常量和抽象方法
  • 接口不能被实例化
  • 接口是需要被实现/继承的,实现类/派生类:
    必须重写接口中的所有抽象方法
  • 一个类可以实现多个接口,用逗号分隔
    若又继承又实现时,应先继承后实现
  • 接口可以继承接口

WARNING

注意:实现的类里面,要尊重重写的两同两小一大规则,也就是重写的方法要加public

TIP

补充:同类型叫继承,不同类叫实现

//接口的演示  
public class InterfaceDemo {  
    public static void main(String[] args) {  
        //Inter1 o1 = new Inter1(); //编译错误,接口不能被实例化  
        Inter6 o2 = new Doo(); //向上造型  不同类型的向上造型
        Inter5 o3 = new Doo(); //向上造型  
    }  
}  
  
  
//演示接口继承接口  
interface Inter5{  
    void show();  
}  
interface Inter6 extends Inter5{  
    void test();  
}  
class Doo implements Inter6{  
    public void show(){}  
    public void test(){}  
}  
  
//演示又继承又实现  
interface Inter3{  
    void show();  
}  
interface Inter4{  
    void test();  
}  
abstract class Boo{  
    abstract void say();  
}  
class Coo extends Boo implements Inter3,Inter4{  
    public void show(){}  
    public void test(){}  
    void say(){}  
}  
  
//演示接口的实现  
interface Inter2{  
    void show();  
    void test();  
}  
class Aoo implements Inter2{  
    public void show(){}  
    public void test(){}  
}  
  
//演示接口的语法  
interface Inter1{  
    public static final int NUM = 5;  
    public abstract void show();  
    int COUNT = 6; //默认public static final  
    void test();   //默认public abstract  
    //int number; //编译错误,常量必须声明同时初始化  
    //void say(){} //编译错误,抽象方法不能有方法体  
}  

# 多态

# 意义

  • 同一类型的引用指向不同的对象时,有不同的实现
    --------行为的多态:cut()、run()、step()...
    • 同一个对象被造型为不同的类型时,有不同的功能
      --------对象的多态:我、你、水...

# 向上造型/自动类型转换:

  • 超类型的引用指向派生类的对象
    • 能造型成为的类型有:超类+所实现的接口
    • 能点出来什么,看引用的类型

# 强制类型转换

成功的条件只有如下两种:

  • 引用所指向的对象就是该类型
    • 引用所指向的对象实现了该接口/继承了该类

WARNING

强转若不符合如上两个条件,则发生ClassCastException类型转换异常

TIP

建议:强转之前先通过instance of来判断引用所指向的对象是否是该类型

//多态的演示  
public class MultiTypeDemo {  
    public static void main(String[] args) {  
        Aoo o = new Boo(); //向上造型(自动类型转换)  
        Boo o1 = (Boo)o;       //引用o所指向的对象就是Boo  
        Inter1 o2 = (Inter1)o; //引用o所指向的对象实现了Inter1  
        //Coo o3 = (Coo)o; //ClassCastException类型转换异常  
        if(o instanceof Coo){ //false  
            Coo o4 = (Coo)o;    注意:引用类型做强转一定要判断
        }  
          
        System.out.println("over");  
    }  
}  
  
interface Inter1{  
}  
class Aoo{  
}  
class Boo extends Aoo implements Inter1{  
}  
class Coo extends Aoo{  
}  

# 内存管理

由JVM来管理

#

  1. 存储new出来的对象(包括实例变量)
  2. 垃圾:没有任何引用所指向的对象,
    垃圾回收器(GC)不定时到内存中清扫垃圾,
    回收过程是透明的(看不到的),不一定一发现垃圾就立刻回收,
    调用System.gc()可以建议虚拟机尽快调度GC来回收垃圾
  3. 实例变量的生命周期:
    创建对象时存在堆中,对象被回收时一并被回收
  4. 内存泄漏:不再使用的对象还没有被及时的回收(小面试题)
    建议:不再使用的对象及时将引用设置为null

#

  1. 正在调用的方法中的局部变量(包括方法的参数)
  2. 调用方法时,在栈中为该方法分配一块对应的栈帧,
    栈帧中存储方法中的局部变量(包括方法的参数),
    方法调用结束时,栈帧被清除,局部变量一并被清除
  3. 局部变量的生命周期:
    方法调用时存储在栈中,方法结束时与栈帧一并被清除

# 方法区

  1. 存储.class字节码文件(包括方法、静态变量)
  2. 方法只有一份,通过this来区分具体的访问对象
/** 
 * 解释同一个类型的引用画等号后,指向堆中的同一个对象 
* 引用类型与基本类型画等号的区别 
 */  
public class RefDemo {  
    public static void main(String[] args) {  
        Student stu1 = new Student();  
        Student stu2 = stu1;  
        stu1.age = 18;  
        stu2.age = 88;  
        System.out.println(stu1.age);  
          
        int a = 1;  
        int b = 2;  
        b=a;  
        System.out.println(a);  
    }  
}  
  
class Student {  
    int age;  
    int name;  
}

# 面向对象总结

# 设计规则

  1. 将所有派生类所共有的属性和行为,抽到超类中-------抽共性
  2. 所有派生类的行为都一样,设计为普通方法
    所有派生类的行为都不一样,设计为抽象方法
  3. 将部分派生类所共有的行为,抽到接口中
    符合既是也是原则时,使用接口
    接口是对继承的单根性的扩展------------多继承

# 面向对象三大特征

  • 封装:
    1. 类:封装的是对象的属性的行为
    2. 方法:封装一段特定的业务逻辑功能
    3. 访问控制修饰符:封装的是具体的访问权限
  • 继承:
    1. 作用:代码复用
    2. 超类:所有派生类所共有的属性和行为
      接口:部分派生类所共有的行为
      派生类:派生类所特有的属性和行为
    3. 单一继承、多接口实现,传递性
  • 多态:
    1. 行为多态(所有抽象方法都是多态的)
      对象多态(所有对象都是多态的)
    2. 向上造型、强制类型转换、instance of
    3. 多态的表现形式:
    • 重写:根据对象的不同来多态
      • 重载:根据参数的不同来多态

# debug

  • 何时用:当程序的运行结果与你的预期不同时,需要调试
  • 调试方式:
    1. 打桩: System.out.println(数据);
    2. Debug调试工具:
    • 添加断点
      • F5:单步调试(会进入到方法中)
        • F6:逐过程调试(不会进入到方法中)
      • F7:结束方法的调试(从方法中返回)
    • F8:直接跳到下一个断点(若后面无断点则结束调试)
    1. 会看两个东西:
    2. 会看变量
    3. 添加监视 (在debug中选中变量,然后右键,选择watch)

# 补充

  • 私有的数据无法使用的时候,就创建一个公开的方法get一下。
  • Java中所有类都会继承object
  • 清理现场就是用原来引用指向一个新创建的对象,原来创建的对象就会被当作垃圾回收
      实例变量
  变量    
  静态变量

注:这里的实例变量就是成员变量
Last Updated: 11/14/2024, 2:46:20 PM