面向对象浅见


面向对象(OO)

三大特征

封装

  • 类:封装对象属性和行为
  • 方法:封装一定的业务逻辑功能
  • 访问控制修饰符:封装具体的访问权限

继承

  • 作用:代码复用

  • 超类:所有派生类共有的属性和行为

    接口:部分派生类共有的属性和行为

    派生类:派生类特有的属性和行为

  • 单一继承、多接口实现,具有传递性

  1. 代码复用,通过extends来实现继承方法
  • 派生类可以访问:派生类的+超类的

  • 超类不能访问派生类的

    规定:构造派生类之前必须先构造超类

  1. 派生类构造中若不调用超类构造,则默认super()调超类无参构造

  2. super:指当当前对象的超类对象

super.成员变量名—————————访问超类的成员变量

super.方法名()——————————–调用超类的方法

super()——————————————调用超类的构造方法

补充

  • super 和 this 不能同时调用方法
  • 类属于引用数据类型
  • 类里面不赋值,默认值即为数组的默认值(整型 0,引用 null,布尔false,浮点型0.0)
  • new n个 对象往往不会在for里面

多态

  • 意义:

    • 同一个对象被造型为不同的类型时,有不同的功能

      —对象多态:我、你、水——————-所有对象都是多态的(明天再详细讨论)

    • 同一类型的引用指向不同的对象时,有不同的实现

      —行为多态:cut(),move(),getImage()……———所有抽象方法都是多态的

  • 向上造型:

    • 超类型的引用指向派生类的对象
    • 能点出来什么,看引用的类型
    • 能造型成为的类型有:超类+所实现的接口
  • 强转类型转换,成功的条件只有如下两种:

    • 引用所指向的对象,就是该类型
    • 引用所指向的对象,实现了该接口或继承了该类
  • 强转时若不满足如上条件,则发生ClassCastException类型转换异常

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

类和对象

现实生活中有很多对象组成,基于对象抽出了类

  • 对象:软件中真是存在的单个个体,具有不同的属性

    类:一类个体

一般有几个对象,我们专门为此创建几个类

  • 对象为具体,类为模子,一个类可以创建多个对象

  • 类中包含

    • 对象的属性/特征——成员变量
    • 对象的行为/动作/功能——方法(同一个类不能相同的方法签名)
  • 成员变量会默认初始值

this用法

  1. this:指代当前对象,哪个对象调用方法它指的就是哪个对象

    ​ 只能用在方法中,方法中访问成员变量之前默认有个this.

    this的用法:

    • this.成员变量名————–访问成员变量

      当成员变量和局部变量同名时,若想访问成员变量则this不能省略,其它一般省略

    • this.方法名()——————调用方法(一般不用,了解)

    • this()—————————-调用构造方法(一般不用,了解)

    class Student {
        //成员变量
        String name;
        int age;
        String address;
        //构造方法
        Student(String name,int age,String address){
            this.name = name;       //zs.name="zhangsan"
            this.age = age;         //zs.age=25
            this.address = address; //zs.address="LF"
        }
    
        //实例方法
        void study(){
            System.out.println(name+"在学习...");
        }
        void sayHi(){
            System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
        }
    }
    
    //构造方法和this的演示
    public class ConsDemo {
        public static void main(String[] args) {
            //Student zs = new Student(); //编译错误,Student类没有无参构造
            Student zs = new Student("zhangsan",25,"LF");
            Student ls = new Student("lisi",26,"JMS");
            zs.sayHi();
            ls.sayHi();
        }
    }

方法和对象

构造方法

构造方法:构造函数、构造器、构建器—————-复用给成员变量初始化代码

  • 作用:给成员变量赋初始值

  • 与类同名,没有返回值类型(连void都没有)

  • 在创建(new)对象时被自动调用

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

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

  • 构造方法可以重载

补充

  • 行参:声明方法变量时

    实参:调用方法时

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

方法的重载(overload)

  • 发生在同一类中,方法名相同,参数列表不同
  • 编译器会根据参数列表的不同自动选择方法

方法的重写(override)

  • 父子类中,方法名相同,参数列表相同(方法签名相同)

  • 在运行时只走子类(new 的 对象)

重写需遵循”两同两小一大”原则:

  • 两同:

    • 方法名相同
    • 参数列表相同
  • 两小:

    • 派生类方法的返回值类型小于或等于超类方法的
      • void和基本类型时,必须相等
      • 引用类型时,可以小于或等于
    • 派生类方法抛出的异常小于或等于超类方法的————API时再说
  • 一大:

    • 派生类方法的访问权限大于或等于超类方法的

    补充: 静态方法不能被重写

方法的向上造型

  • 左边为超类容器,右边为派生类的参数
  • 只能调用超类的数据、方法

官方

  • 超类型的引用指向派生类的对象
  • 能点出来什么,看引用的类型
class 餐馆{
    void 做餐(){ 做中餐 }   
}
//1)我还是想做中餐-------------不需要重写
class Aoo extends 餐馆{
}
//2)我想改做西餐---------------需要重写
class Aoo extends 餐馆{
    void 做餐(){ 做西餐 }
}
//3)我想在中餐之上加入西餐-------需要重写(先super中餐,再加入西餐)
class Aoo extends 餐馆{
    void 做餐(){
        super.做餐(); + 做西餐
    }
}

抽象方法

​ abstract修饰,只有方法的定义,没有具体的实现(连{}都没有)

抽象类

abstract修饰,包含抽象方法的类必须是抽象类,不能被实例化

​ 需要被继承,派生类:重写所有抽象方法

​ 封装共有的属性和行为—-代码复用 为所有派生类提供统一的类型—–向上造型

​ 为所有派生类提供统一的入口(能点出来),强制必须重写

接口

  • 一种引用数据类型,一种标准

  • 由interf来定义(非修饰)

  • 只包含常量和抽象方法

  • 不能被new对象,不能实例化

  • 接口是要被实现(implements)的,实现/派生类:必须重写所有抽象方法,必须加关键字public

  • 一个类可以实现多个接口,用逗号分隔。若又继承又实现,先继承再实现

  • 接口可以继承接口

    补充

    • 类和类 继承
    • 接口和接口 继承
    • 类和接口 实现

内外部类

  1. 成员内部类:应用率低

    • 类中套类,外面的称为外部类,里面的称为内部类

    • 内部类通常只服务于外部类,对外不具备可见性

    • 内部类对象通常在外部类中创建

    • 内部类中可以直接访问外部类的成员(包括私有的)

      内部类中有个隐式的引用指向了创建它的外部类对象—外部类名.this——API时会用

    public class InnerClassDemo {
        public static void main(String[] args) {
            Mama m = new Mama();
            //Baby b = new Baby(); //编译错误,内部类对外不具备可见性
        }
    }
    
    class Mama{ //外部类
        private String name;
        void create(){
            Baby b = new Baby(); //正确
        }
        class Baby{ //内部类
            void showName(){
                System.out.println(name);
                System.out.println(Mama.this.name); //Mama.this指代当前对象的外部类对象
                //System.out.println(this.name); //编译错误,this指代当前Baby对象
            }
        }
    }
  2. 匿名内部类:重点—————–大大简化代码

    • 若想创建一个类(派生类)的对象,并且对象只被创建一次,可以做成匿名内部类
    • 匿名内部类中不能修饰外面局部变量的值,因为在此处该变量会默认为final的
    public class AnonInnerClassDemo {
        public static void main(String[] args) {
            //new Aoo(); //创建Aoo对象
            //new Aoo(){}; //创建Aoo的派生类的对象
    
            //1)创建了Aoo的一个派生类,但是没有名字
            //2)为该派生类创建了一个对象,名为o1
            //3)大括号中的为派生类的类体
            Aoo o1 = new Aoo(){ //向上造型
            };
    
            //1)创建了Aoo的一个派生类,但是没有名字
            //2)为该派生类创建了一个对象,名为o2
            //3)大括号中的为派生类的类体
            Aoo o2 = new Aoo(){
    
            };
    
            int num = 5;
            num = 55;
            //1)创建了Boo的一个派生类,但是没有名字
            //2)为该派生类创建了一个对象,名为o3
            //3)大括号中的为派生类的类体
            Boo o3 = new Boo(){
                void show(){ //重写Boo类的抽象方法
                    System.out.println("showshow");
                    //num = 66; //编译错误,在此处会默认num为final的---API时会用
                }
            };
            o3.show();
        }
    }
    
    abstract class Boo{
        abstract void show();
    }
    
    abstract class Aoo{
    }

关键字

修饰关键词

  1. package

    • 避免类冲突
    • 同包中类不可以同名,不同包的类可以
    • 类的全称包名.类名
    • 建议所有包名小写
  2. impor

    • 同包中导入类,可以用
      • 不同包导入类,需要导入包
      • 或者引用类的全称
  3. 访问控制符(导入包 不起作用)

    • public 公开的

    • protected 本类 派生类 同包类

    • 默认(不写) 本类 同包类 ——不建议

    • private 本类

  4. finall:最终的、不可更改的——单独利用率低

    • 修饰变量:变量不能被改变,为常量
    • 修饰方法:方法不能被重写
    • 修饰类: 类不能被继承
  5. static :静态变量

    • 属于类

    ​ 成员变量分为两种

    • 实例变量:没有static修饰,属于对象的,存储在堆中,有几个对象就有几个实例变量

    ​ 需要通过引用打点来访问

    • 静态变量:

      • 由static修饰,属于类,存储在方法区中,只有一份

      • 通过类名打点来访问

      • 何时用:所有对象需要共享的数据(图片,音频,视频)

    • 静态方法:

      • 前两条同上
      • static将方法体隐式的this删去,不能直接访问实例成员(即静态方法只能访问静态成员)
      • 何时用:方法操作和对象无关(不需要访问对象
    • 静态块

      • 由static修饰
      • 属于类,在类加载的时候自动执行,一个类只加载一次,所以静态块也加载一次
      • 何时用:初始化/加载静态资源(图片、音频、视频)
static{
    语句块
}
  1. static final(常量)

    • 必须声明同时初始化
    • 由类名来访问
    • 存储在常量池中,常量池存储在堆中
    • 建议:常量名所有字母大写,多个单词用_分隔
    • 编译器在编译时会将常量直接替换为具体的值,效率高
  • 何时用:数据永远不变且经常使用,例如状态

String类(举例)

  • java.lang.String 用final修饰,不能被继承(java.lang包可以直接引用)
  • 采用Unicode编码,一个字符2字节
  • 底层封装了字符的数组
  • 字符串一旦创建,对象的内容永远无法改变,但字符串的引用可以重新赋值——不变对象

常量池

  • java对字符串的优化措施:常量池(堆中)

  • 推荐使用字面量/直接量来创建字符串,并且会缓存所有以字面量形式创建的字符串对象到常量池中,当使用相同字符量在创建字符串会重用对象以减少内存开销

  • 用来存放字符串的地址 ,指向非常量池中的堆中

  • 只能通过字面量相连才会复用,不用使用引用的对象名

String s = new String("hello");
// 以上创建了几个对象
/** * 答 2个 
* 第一个: 字面量“hello” 
* 创建一个字面量对象表示“hello”,并将其存入常量池
* 第二个:
* new String() 时会再创建一个字符串对象, */

方法

  • 对象名.length(): 获取字符串长度,return int

  • 对象名.trim(): 去除当前字符串两边的空白字符,return一个新对象

  • 对象名.toUpperCase():将当前符号中的英文转换为全大写

    对象名.toLowerCase():将当前符号中的英文转换为全小写

    全部return String

  • 对象名.startsWith(”sth”):判断字符串是否以”sth“ 开头,return boolea类型

    对象名.endsWith(”sth”):判断字符串是否以” sth “ 结尾,return boolea类型

    区分大小写

  • 对象名.charAt(int index) : 返回下标处的字符,return char

    1. 对象名.indexOf(String str ) :检索str在String开始的下标,return int

    2. 对象名.indexOf(String str, int fromIndex ) :检索str在String从下标”fromIndex“以后的下标,return int。其中return的值为差值

    3. 对象名.lastIndexOf(String str): 检索str在String最后一次的下标,return int

    若找不到目标字符串,返回-1

  • 对象名.substring(int a,int b) :截取下标 a到 b 之间的字符串 return String

    对象名.substring(int a) :截取从下标a一直到末尾, return String

  • String.valueOf(a):将基本数据类型a所代表的值转换为String,return String

StringBuilder类

  • 由于String是不变对象,每次修改内容要创建新对象,因此String不适合做频繁修改操作,为了解决这个问题,java提供了StringBuilder类。

  • StringBuilder是专门用于修改字符串的一个API,内部维护一个可变的char数组,修改都是在这个数组上进行的,修改速度、性能优秀,并且提供了修改字符串的常见的方法:增、删、改、插

    //String不适合频繁修改内容
    String s = "a";
    for(int i=0;i<10000000;i++){
        s = s+i;
    }
    System.out.println("执行完毕");
    
    //用StringBuilder可以提高修改字符串的性能
    StringBuilder builder = new StringBuilder("a");
    for(int i=0;i<10000000;i++){
        builder.append(i);
    }
    System.out.println("执行完毕");
  • StringBuilder常用方法:

    • append():追加内容
    • replace():替换部分内容
    • delete():删除部分内容
    • insert():插入内容
    String str = "好好学习java";
    //复制str中的内容到builder中-----好好学习java
    StringBuilder builder = new StringBuilder(str);
    
    //append():追加内容
    builder.append(",为了找个好工作!");
    System.out.println(builder); //好好学习java,为了找个好工作!
    
    //replace():替换部分内容
    builder.replace(9,16,"就是为了改变世界"); //替换下标9到15的
    System.out.println(builder); //好好学习java,就是为了改变世界!
    
    //delete():删除部分内容
    builder.delete(0,8); //删除下标0到7的
    System.out.println(builder); //,就是为了改变世界!
    
    //insert():插入操作
    builder.insert(0,"活着"); //从下标0的位置插入
    System.out.println(builder); //活着,就是为了改变世界!
    

文章作者: hyy
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hyy !
  目录