面向对象高级三

目录:

  1. 内部类
  2. 枚举
  3. 泛型
  4. 常用API(一)

内部类

  • 是类中的五大成分之一(成员变量,方法,构造器,内部类,代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
  • 场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。
public class Car{
    //内部类
    public class Engine{
    }
}
内部类有四种形式
  1. 成员内部类

就是类中的一个普通成员,类似前面学过的普通的成员变量、成员方法。

public class Outer{
    //成员内部类
    public class Inner{
    }
}


public static void main(String[] args){
    //创建内部类的对象的语法
    //外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...);
    Outer.Inner in = new.Outer().new Inner();
}

成员内部类中访问其他成员的特点:

  1. 和前面学过的实例方法一样,成员内部类的实例方法中,同样可以直接访问外部类的实例成员、静态成员。
  2. 可以在成员内部类的实例方法中,拿到当前外部类对象,格式是:外部类名.this
public class Outer{
    private int age = 99;
    public class Inner{
        private int age = 88;
        
        public void show(){
            int age = 77;
            //三种变量的访问方法
            System.out.println(age);//77
            System.out.println(this.age);//88
            System.out.println(Outer.this.age);//99
        }
    }
}


public static void main(String[] args){
    //创建内部类的对象的语法
    Outer.Inner in = new.Outer().new Inner();
}
  1. 静态内部类

有static修饰的内部类,属于外部类自己持有。

public class Outer{
    //静态内部类
    public static class Inner{
        
    }
}

public static void main(String[] args){
    //创建内部类的对象的语法
    //外部类名.内部类名 对象名 = new 外部类(...).内部类(...);
    Outer.Inner in = new.Outer().Inner();
    //相比较成员内部类这里内部类不需要new只要new外部类就行了
}

静态内部类中访问外部类成员的特点

  • 可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员。
  1. 局部内部类

局部内部类是定义在方法中、代码块中、构造器等执行体中。

public class Test{
    public static void main(String[] args){
        
    }
    
    public static void go(){
        class A{
            
        }
        
        abstract class B{
            
        }
        
        interface C{
            
        }
    }
}

局部内部类,鸡肋语法,不建议使用。

  1. 匿名内部类(重点)

就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字。

new 类或接口(参数值..){
    类体(一般是方法重写);
}

eg:

public static void main(String[] args){
    Animal a = new Animal(){
        @Override
        public void cry(){
            System.out.println("猫叫")
        }
    };
    
    a.cry();
}
abstract class Animal{
    public abstract void cry();
}

特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象。

作用:用于更方便的创建一个子类对象。

匿名内部类在开发中的使用场景
  • 通常作为一个参数传输给方法。
需求:猫、狗参加游泳比赛。
public interface Swimming{
    void swim();
}
public class Test{
    public static void main(String[] args){
        Swimming s1 = new Swimming(){
            @Override
            public void swim(){
                System.out.println("狗游的飞快");
            }
        }
        go(s1);
        
        go(new Swimming(){
            @Override
            public void swim(){
                System.out.println("猫游的一般");
            }
        });
    }
    
    public static void go(Swimming s){
        System.out.println("开始。。。。。");
        s.swim();
    }
}
# 匿名内部类小结
1. 匿名内部类的书写格式是什么样的?
		new 类或接口(参数值...){
			类体(一般是方法重写);
		}
		new Animal(){
			@Override
			public void cry(){
			
			}
		}
2. 匿名内部类有什么特点?
		- 匿名内部类本质就是一个子类,并会立即创建出一个子类对象
3. 匿名内部类有什么作用、应用场景?
		- 可以更方便的创建出一个子类对象。
		- 匿名内部类通常作为一个参数传输给方法。

枚举

枚举是一种特殊类。

枚举类的格式:

修饰符 enum 枚举类名 {
    名称1,名称2,...;
    其他成员...
}

public enum A{
    X,Y,Z;
    ...
}

注意:

  • 枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。
  • 这些名称,本质是常量,每个常量都会记住枚举类的一个对象。

枚举类的特点:

  • 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
  • 枚举类的构造器都是私有的(写不写都是私有的),因此,枚举类不能创建对象。
  • 每句都是最终类,不可以被继承。
  • 枚举类中,从第二行开始,可以定义类的其他各种成员。
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承java.lang.Enum类的,从Enum类也会继承到一些方法。
public class Test {
    public static void main(String[] args) {
        A a1 = A.X;
        System.out.println(a1);

        //枚举类的构造器是私有的,不能对外创建对象
        //A a = new A();

        //枚举类的第一行都是常量,记住的是枚举类的对象
        A a2= A.Y;

        //枚举类提供了一些额外的API
        A[]  as = A.values();
        A a3 = A.valueOf("Z");
        System.out.println(a3.name());//Z
        System.out.println(a3.ordinal());//索引
    }
}
public enum A {
    //枚举第一行必须罗列的是枚举对象的名字。
    X, Y, Z;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

抽象枚举

public enum B {
    //因为这个枚举中有抽象方法,所以在枚举对象必须在声明的时候就创建。
    X(){
        @Override
        public void go() {
            
        }
    },Y("zhang"){
        @Override
        public void go() {
            System.out.println(getName() + "...");
        }
    };


    private String name;

    B() {
    }
    
    B(String name) {
    	this.name = name;
    }

    public abstract void go();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Test {
    public static void main(String[] args) {
        B y = B.Y;
        y.go();
    }
}

因为枚举构造器私有,所以可以直接用枚举写单例。

public enum C{
    X;
}

枚举的常见应用场景:

  • 用来表示一组信息,然后作为参数进行传输。

选择定义单个的常量来表示一组信息,并作为参数传输。

  • 参数值不受约束

选择定义枚举表示一组信息,并作为参数传输

  • 代码可读性好,参数值得到了约束,对使用者更友好,建议使用!
public enum Sex {
    BOY, GIRL;
}

public class Test {
    public static void main(String[] args) {
        check(Sex.BOY);
    }

    //进行分类,使用枚举类限制了传入信息。
    public static void check(Sex sex) {
        switch (sex) {
            case BOY:
                System.out.println("针对男性用户操作业务");
                break;
            case GIRL:
                System.out.println("针对女性用户操作业务");
                break;
        }
    }
}

泛型

  • 定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口,泛型方法,他们统称为泛型。

  • 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。

  • 泛型的本质:把具体的数据类型作为参数传给类型变量。

泛型类
修饰符 class 类名<类型变量,类型变量, ...>{
    
}

pubic class ArrayList<E>{
    
}
泛型接口
修饰符 interface 接口名<类型变量,类型变量, ...>{
    
}

public interface A<E>{
    
}
interface Data<E extends People>{
    void add(E e);
    ArrayList<E> getByName(String name);
}

class StudentData implements Data<Student>{

    @Override
    public void add(Student student) {
        
    }

    @Override
    public ArrayList<Student> getByName(String name) {
        return null;
    }
}

class TeacherData implements Data<Teacher>{

    @Override
    public void add(Teacher teacher) {
        
    }

    @Override
    public ArrayList<Teacher> getByName(String name) {
        return null;
    }
}

注意:类型变量建议用大写的英文字母,常用的有:E,T,K,V等。

泛型方法、泛型通配符、上下限
修饰符 <类型变量,类型变量, ...> 返回值类型 方法名(形参列表){
    
}

public static <T> void test(T t){
    
}

泛型通配符

  • 就是?,可以在“使用泛型”的时候代表一切类型;E T K V是在定义泛型的时候使用的。
//  ? 通配符:在使用泛型的时候可以代表一切类型。
public static void go(ArrayList<?> cars){	}

上下限

  • 泛型上限:? extends Car ?能接收的必须是Car或者其子类
  • 泛型上限:? super Car ?能接收的必须是Car或者其父类
//  ? extends Car (上限)
public static void go2(ArrayList<? extends Car> cars){	}
//  ? super Car (下限)
public static void go(ArrayList<? super Car> cars){	}
泛型擦除问题、包装类介绍

泛型的擦除问题和注意事项

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
  • 泛型不支持基本数据类型,只能支持对象类型(引用数据类型)
    • ​ 解决方案使用基本数据类型的包装类(Integer,Double…)

java.lang包下的常用API

Object

Object类是Java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用Object类中提供的一些方法。

Object类常见的方法

方法名说明
public String toString()返回对象的字符串表示形式。
public boolean equals(Object o)判断两个对象是否相等。
protected Object clone()对象克隆
# equals的逻辑
1. 判断两个对象是否地址一样,一样直接返回true
2. 判断传入的o是null直接返回false,或者比较者(this)的类型与被比较者的类型不一样,返回false
3. 保证类型一致且不为空,开始逐个比较对象的属性。

toString存在的意义:toString()方法存在的意义就是为了被子类重写,以便返回对象具体的内容。

equals存在的意义:直接比较两个对象的地址是否相同完全可以用“==”替代equals,equals存在的意义就是为了被子类重写,以便子类自己来定制比较规则(比如比较对象内容)。

clone:当某个对象调用这个方法时,这个方法会复制一个一模一样的新对象返回。

概念补充:

浅克隆:拷贝出的新对象,与原对象中的数据一模一样(引用类型拷贝的只是地址)
深拷贝:对象中基本类型的数据直接拷贝。对象中的字符串数据拷贝的还是地址(因为字符串放在了常量池中)。对象中还包含的其他对象,不会拷贝地址,会创建新对象。

Objects

Objects是一个工具类,提供了很多操作对象的静态方法给我们使用。

方法名说明
public static boolean equals(Object a, Object b)先做非空判断,再比较两个对象
public static boolean isNull(Object obj)判断对象是否为null,为null返回true,反之
public static boolean nonNull(Object obj)判断对象是否不为null,不为null则返回true,反之

Objects.equals源码://这样更加安全

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}
包装类

为什么要有包装类?

包装类就是把基本类型的数据包装成对象。

基本数据类型对应的包装类(引用数据类型)
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

包装类的使用

Integer a = new Integer(12);//已经舍弃

Integer b = Integer.valueOf(12);

自动装箱和自动拆箱

//自动装箱: 可以自动把基本类型的数据转换成对象
Integer a = 12;
//自动拆箱: 可以自动把包装类型的对象转换成对应的基本数据类型。
int b = a;

包装类存在的意义是什么?

泛型和集合不支持基本数据类型,只能支持引用数据类型。

ArrayList<Integer> list = new ArrayList<>();
list.add(12);
list.add(21);//自动装箱

int res = list.get(1);//自动拆箱

自动装箱:基本数据类型可以自动转换为包装类型。

自动拆箱:包装类型可以自动转换为基本数据类型。

包装类的常见操作:

  • 可以把基本数据类型转换成字符串类型

    • public static String toString(doublic d)
    • public String toString
  • 可以把字符串类型的数值转换成数值本身对应的数据类型。

    • public static int parseInt(String s)
    • public static Integer valueOf(String s)
StringBuilder,StringBuffer
  • StringBuilder代表可变字符串对象,相当于一个容器,它里面装的字符串可以改变的,就是用来操作字符串的。
  • 好处:StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁。
构造器说明
public StringBuilder()创建一个空白的可变的字符串对象,不包含任何内容
public StringBuilder(String str)创建一个指定字符串内容的可变字符串对象。
方法名称说明
puclic StringBuilder append(任意类型)添加并返回StringBuilder对象本身
puclic StringBuilder reverse()将对象的内容反转
public int length()返回对象内容长度
public int toString()通过toString()就可以实现把StringBuilder对象转换为String

为什么操作字符串建议使用StringBuilder,而不是String?

  • 对于字符串相关的操作,如果频繁的拼接、修改等,建议使用StringBuilder,效率更高!
  • 注意:如果操作字符串较少,或者不需要操作,以及定义字符串变量,还是建议使用String。

StringBuffer又是什么?

  • StringBuffer的用法和StringBuilder是一摸一样的。
  • 但是StringBuffer是现成安全的,StringBuilder是线程不安全的。

什么是线程安全?

就是在操作某个方法的时候只能有一个线程来操作,一定程度会降低效率。

案例

设计一个方法,用于返回任意整型数组的内容,要求返回的数组内容格式:[11, 22, 33]

分析:

  1. 方法是否需要接收数据? 需要接受整型数组

  2. 方法是否需要返回类型? 需要的是返回后的拼接结果。

  3. 方法内部:遍历数组的数据,把遍历到的数据都拼接起来,使用StringBuilder来完成。

public class Test1 {
    public static void main(String[] args) {
        int[] arr = new int[5];
        arr[0] = 11;
        arr[1] = 22;
        arr[2] = 33;
        arr[3] = 44;
        arr[4] = 55;

        String info = getArrInfo(arr);
        System.out.println(info);
    }

    public static String getArrInfo(int[] arr) {
        //判断为空
        if (arr == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder("[");
        //遍历添加信息
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                sb.append(arr[i]);
            } else
                sb.append(arr[i]).append(", ");
        }
        //信息结尾
        sb.append("]");
        //返回结果
        return sb.toString();
    }
}
StringJoiner
  • JDK8开始才有的,跟StringBulider一样,也是用来操作字符串的,也可以看成一个容器,创建之后里面的内容是可变的。
  • 好处:不仅能提高字符串的的操作效率,并在有些场景下使用它操作字符串,代码会更简洁。
构造器说明
public StringJoiner(间隔符号)创建一个StringJoiner对象,指定拼接时的间隔符号。
public StringJoiner(间隔符号, 开始符号, 结束符号)创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号。
方法名称说明
public StringJoiner add(添加的内容)添加数据,并返回对象本身
public int length()返回长度(字符个数)
public String toString()返回一个字符串(该字符串就是拼接之后的结果)
public class Test1 {
    public static void main(String[] args) {
        int[] arr = new int[5];
        arr[0] = 11;
        arr[1] = 22;
        arr[2] = 33;
        arr[3] = 44;
        arr[4] = 55;
        System.out.println(getArrInfo2(arr));
    }

    public static String getArrInfo2(int[] arr) {
        //判断为空
        if (arr == null) {
            return null;
        }
        StringJoiner sj = new StringJoiner(",", "[", "]");
        //遍历添加信息
        for (int i = 0; i < arr.length; i++) {
            sj.add(arr[i] + "");
        }
        //返回结果
        return sj.toString();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黎丶辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值