【day03-内部类、泛型、常用API】

1. 内部类

        1.1 内部类的概述

                内部类

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

1.2 内部类分为四种

 成员内部类[了解]:位于一个类里面成员位置的类
 静态内部类[了解]:使用 static 修饰的成员内部类
 局部内部类[了解]:在方法里面定义的类

        匿名内部类[重点]:一种特殊的局部内部类

        成员内部类

         

                静态内部类 

                 局部内部类 

                局部内部类是定义在方法中、代码块中、构造器等执行体中 (用到的不多,前三个大多是了解)

                

                匿名内部类

                这是一种特殊的局部内部类

                所谓匿名:指的是程序员不需要为这个类声明名字

                作用:更方便的创建一个子类对象(简化操作类、接口的代码)

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

                场景:  通常作为一个参数传输给方法

/*
匿名内部类应用场景------作为方法的参数进行传递
    如果一个方法将一个抽象类/接口最为参数,那我们可以直接传递该抽象类/接口的匿名内部类对象

*/
public class Demo {

    public static void main(String[] args) {
        //需求1: 调用test1方法, 打印出 狗在吃肉
        test1(new Animal() {
            @Override
            public void eat() {
                System.out.println("狗吃大刀肉");
            }
        });
        //需求2: 调用test2方法, 打印出 乌鸦在喝水
        test2(new Bird() {
            @Override
            public void drink() {
                System.out.println("乌鸦喝矿泉水");
            }
        });
    }

    public static void test1(Animal animal) {
        animal.eat();
    }

    public static void test2(Bird bird) {
        bird.drink();
    }
}

//抽象类
abstract class Animal {
    public abstract void eat();
}

//接口
interface Bird {
    void drink();
}

2. 泛型

        2.1 泛型

                定义类、接口、方法时,同时声明的类型变量(如:<E> ,称为泛型。 

/*
泛型
    定义
        定义类、接口、方法时,同时声明的类型变量(如:<E>) ,称为泛型。

    作用
        泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!
        这样可以避免强制类型转换,及其可能出现的异常。
    本质
        把具体的数据类型作为参数传给类型变量

    分类
        泛型类、泛型接口、泛型方法
*/
public class Demo {
    public static void main(String[] args) {
        //需求1. 定义一个ArrayList不设置泛型, 保存元素, 然后遍历求元素的长度
        ArrayList list1 = new ArrayList();
        list1.add("关羽");
        list1.add(12.12);
        list1.add(1234);
        list1.add(true);
        for (int i = 0; i < list1.size(); i++) {
            Object object = list1.get(i);
            String str = (String) object;
        }
        //需求2. 定义一个ArrayList, 设置泛型为String, 保存数据, 然后遍历求元素的长度
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("张飞");
        list2.add("典韦");
//        list2.add(123);会报错
        for (int i = 0; i < list2.size(); i++) {
            System.out.println(list1.get(i));
        }
    }
}

        2.2泛型类

                 

/*
泛型类
    在定义类的时候设置泛型, 然后在后续方法上使用

格式
    修饰符 class 类名<类型变量,类型变量,…> {

    }

注意:
    类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
*/
public class Demo {
    public static void main(String[] args) {
        MyList<String> myList = new MyList();
        myList.add("123");
        myList.add("关羽");
    }
}

//自定义一个泛型类, 模仿ArrayList的add和get功能
class MyList<E> {
    //1.定义一个长度为10的数组
    private E[] arr =(E[]) new Object[10];
    //2.定义一个存放元素的索引
    private int index = 0;

    //方法1:添加元素
    public void add(E e) {
        arr[index] = e;
        //索引加一
        index++;
    }

    public E get(int index) {
        return arr[index];
    }
}

        2.3泛型接口

//需要自己创建两个类(学生,老师)
/*
泛型接口
    在定义接口的时候声明泛型

格式
    修饰符 interface 类名<类型变量,类型变量,…> {

    }

注意
    类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
*/
public class Demo {
    public static void main(String[] args) {
        CommonInterface c1 = new TeacherInterfaceImpl();
        c1.findByName("老张");

    }
}

//需求: 定义一个接口(接口中拥有两个功能: 保存对象数据和根据名称返回对象)
//谁实现这个接口,谁就需要对两个功能做就提实现
interface CommonInterface<E>{
    void add(E e);
    E findByName(String name);
}


interface TeacherInterface{
    void add(Teacher teacher);//保存
    Teacher findByName(String name);//查询
}

class TeacherInterfaceImpl implements CommonInterface<Teacher>{


    @Override
    public void add(Teacher teacher) {

    }

    @Override
    public Teacher findByName(String name) {
        System.out.println("老张");
        return null;
    }
}



interface StudentInterface{
    void add(Student student);//保存
    Student findByName(String name);//查询
}

class StudentInterfaceImpl implements CommonInterface<Student>{



    @Override
    public void add(Student student) {

    }

    @Override
    public Student findByName(String name) {
        return null;
    }
}

        2.4泛型接口

                泛型方法中格式是接收所有类型,实际应用中会限制他的类型,用来约束传过来的的参数,这样就起到了泛型的作用。 

public class Demo {

    public static void main(String[] args) {
        //需求: 编写一个将两个相同类型的对象放入一个集合的方法
        ArrayList<String> list1 = add("1", "2");
        Teacher t1 = new Teacher("1");
        Teacher t2 = new Teacher("2");
        ArrayList<Teacher> list2 = add(t1, t2);//Teacher类
        Student s1 = new Student("1");
        Student s2 = new Student("2");
        ArrayList<Student> list3 = add(s1, s2);//Student类

    }

    public static <E> ArrayList<E> add(E a, E b) {
        ArrayList<E> list = new ArrayList<>();
        list.add(a);
        list.add(b);
        return list;
    }

    //将两个字符串放入一个集合
    public static ArrayList<String> add(String a, String b) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add(a);
        arrayList.add(b);
        return arrayList;
    }

    //将两个Teacher放入一个集合
    public static ArrayList<Teacher> add(Teacher a, Teacher b) {
        ArrayList<Teacher> arrayList = new ArrayList<>();
        arrayList.add(a);
        arrayList.add(b);
        return arrayList;
    }

    //将两个Student放入一个集合
    public static ArrayList<Student> add(Student a, Student b) {
        ArrayList<Student> arrayList = new ArrayList<>();
        arrayList.add(a);
        arrayList.add(b);
        return arrayList;
    }

}

class Teacher {
    private String name;
    public Teacher(String name) {
        name = name;
    }
}

class Student {
    private String name;
    public Student(String name) {
        name = name;
    }
}

        2.4泛型通配符、上下限

                 方法中可以写成T extends 类名,这样可以限制传输过来的类的类型(作业当中会用到)

 

/*
通配符
    <?>,可以在使用泛型的时候,代表一切类型
泛型上限
    <?extends 类名>,能接收指定类型利子类
泛型下限
    <?super类名>,接收指定类型和父类
*/
public class Demo {
    public static void main(String[] args) {
        //数据准备
        ArrayList<Animal> animals = new ArrayList();
        ArrayList<Dog> dogs = new ArrayList<Dog>();
        ArrayList<Person> persons = new ArrayList<Person>();
        ArrayList<Teacher> teachers = new ArrayList<Teacher>();
        ArrayList<Student> students = new ArrayList<Student>();
        //需求1
        m1(animals);
        m1(dogs);
        m1(persons);
        m1(teachers);
        m1(students);
        //需求2
        //m2(animals);
        //m2(dogs);
        m2(persons);
        m2(teachers);
        m2(students);
        //需求3
        m3(animals);
        //m3(dogs);
        m3(persons);
        //m3(teachers);
        //m3(students);
    }

    //需求1: 定义一个方法m1,参数为一个ArrayList集合,集合中可以存放任意类型的参数
    public static void m1(ArrayList<?> list){
        System.out.println(list.size());
    }
    //需求2: 定义一个方法m2,参数为一个ArrayList集合,集合中可以存放Person及其子类型的参数
    public static void m2(ArrayList< ? extends Person> list){//泛型上限
        System.out.println(list.size());
    }
    //需求3: 定义一个方法m3,参数为一个ArrayList集合,集合中可以存放Person及其父类型的参数
    public static void m3(ArrayList< ? super Person> list){//泛型下限
        System.out.println(list.size());
    }

}

//动物
class Animal{

}
//狗
class Dog extends Animal{

}
//人
class Person extends Animal{

}
//老师
class Teacher extends Person{

}
//学生
class Student extends Person{

}

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

3. 常用API

        3.1 常量

 

/*
常量:
    使用了static final修饰的成员变量就被称为常量, 通常用于记录系统的配置信息。

命名规范:
    单词全部大写,多个之间使用_连接

优点:
    1. 代码可读性更好,可维护性也更好。
    2. 程序编译后,出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
*/
public class Demo {
    public static void main(String[] args) {
        System.out.println(Constant.STUDENT_NAME);
//        Constant.STUDENT_NAME = "新加坡";//Cannot assign a value to final variable 'STUDENT_NAME' 不能被修改
        //需求2: 保存学生的性别, 为提高性能,经常使用0和1表示性别
        System.out.println(Constant.STUDENT_MALE);
    }
}

//需求1: 定义一个常量,记录学校的名称
class Constant {
    public static final String STUDENT_NAME = "马家堡";
    public static final int STUDENT_MALE = 0;
    public static final int STUDENT_FEMALE = 1;
}

         3.2 枚举

/*
枚举
    Java提供的一种用于简洁表示一些固定值的类

枚举类格式
    修饰符 enum 校举类名{
        校举顶1,校举项2..;
        其他成员..
    }

枚举的特点
    1、枚举类的第一行只能罗列一些名称,且默认都是常量,每个常量记住的就是枚举类的一个对象
    2、枚举类的构造器都是私有的(自己提供也只能是私有的),因此枚举类对外不能创建对象
    3、枚举都是最终类,不能被继承
    4、枚举类中,从第二行开始,可以定义类的其他成员
    5、编译器为枚举类新增了几个方法,并且所有校举类都是java.lang.Enum的子类,所以可以使用父类的方法
*/
public class Demo {
    public static void main(String[] args) {
        //1. 需求: 创建Student对象,使用set赋值为:张三,MAN
        Student student = new Student();
        student.setName("张三");
        student.setSex(Sex.MALE);//java: 不兼容的类型: java.lang.String无法转换为枚举.Sex
        System.out.println(student);

    }
}

enum Sex {
    MALE,FEMALE
}
class Student {
    private String name;
    private Sex sex;

    public void setSex(Sex sex) {
        this.sex = sex;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

         3.2 Object类 

/*
Object类是Java中所有类的祖宗类,类中的方法可以被所有Java类所使用
    String toString(): 返回对象的字符串表示形式, 默认打印对象的内存地址, 一般用于子类重写返回对象的指定格式
    boolean equals(Object o): 判断两个对象是否相等, 默认比较两个对象的内存地址, 一般用于子类重写自定义比较规则
*/
public class Demo {
    public static void main(String[] args) {
        //创建Student1
        Student student1 = new Student("老张", 18);
        //打印student1默认就是用toString,打印的是student1的地址值,我们要输出他的内容需重写toString()
        System.out.println(student1.toString());//Student{name='老张', age=18}
        System.out.println("————————————————");
        Student student2 = new Student("老张", 18);
        System.out.println(student2);//Student{name='老张', age=18}
        System.out.println("————————————————");
        //equals()默认比较的是两个对象的地址值,我们判断他的内容是否相等需重写equals
        boolean equals = student1.equals(student2);
        System.out.println("两个对象是否相等:" + equals);//两个对象是否相等:true (重写完之后比较的就是内容)
    }
}

class Student {
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
}
                浅克隆

 

/*
Object类常用方法
    protected object clone() 对象克隆

浅克隆: 将基本类型数值、引用类型的地址都拷贝一份
    1、子类必须实现cloneable接口(标记接口),否则运行报CloneNotSupportedException
    2、子类重写clone方法, 在里面直接调用父类提供的clone方法

深克降: 将基本类型数值、字符串的地址都拷贝一份; 其他引用类型的数据,会创建新对象完成拷贝(拷贝出新的地址)
    1、子类必须实现cloneable接口(标记接口),否则运行报CloneNotSupportedException
    2、子类重写clone方法, 在里面直接调用父类提供的clone方法
    3、在clone方法中, 将克隆得到的对象中的引用类型重新手动clone一下再复制到对象中
*/
public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        //1. 创建学生对象
        Student student = new Student(1, "张三", "admin", new double[]{20, 30, 50});
        System.out.println(student);//Student@4eec7777
        System.out.println(student.id);
        System.out.println(student.username);
        System.out.println(student.password);
        System.out.println(student.scores);//[D@3b07d329

        //2. 克隆一个学生对象
        System.out.println("=====================克隆对象=============================");
        Student cloneStudent = (Student)student.clone();
        System.out.println(cloneStudent);//Student@41629346
        System.out.println(cloneStudent.id);
        System.out.println(cloneStudent.username);
        System.out.println(cloneStudent.password);
        System.out.println(cloneStudent.scores);//[D@3b07d329
    }
}

class Student implements Cloneable{
    int id;
    String username;
    String password;
    double[] scores;

    public Student(int id, String username, String password, double[] scores) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.scores = scores;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
                深克隆

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        //1. 创建学生对象
        Student student = new Student(1, "张三", "admin", new double[]{20, 30, 50});
        System.out.println(student);//Student@4eec7777
        System.out.println(student.id);
        System.out.println(student.username);
        System.out.println(student.password);
        System.out.println(student.scores);//[D@3b07d329

        //2. 克隆一个学生对象
        System.out.println("=====================克隆对象=============================");
        Student cloneStudent = (Student)student.clone();
        System.out.println(cloneStudent);//Student@41629346
        System.out.println(cloneStudent.id);
        System.out.println(cloneStudent.username);
        System.out.println(cloneStudent.password);
        System.out.println(cloneStudent.scores);//浅克隆:[D@3b07d329//深克隆:[D@404b9385
    }
}

class Student implements Cloneable{
    int id;
    String username;
    String password;
    double[] scores;

    public Student(int id, String username, String password, double[] scores) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.scores = scores;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //1.调用父类的克隆方法,创建一个浅克隆对象
        Student clone = (Student) super.clone();
        //2.对浅克隆对象中存在地址问题的引用类型数据,重新赋值
        clone.scores = clone.scores.clone();
        //3.返回对象
        return clone;
    }
}

          3.3 Objects类

 

          3.4 包装类

 

Java泛型Java 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。泛型是将类型参数化,实现代码的通用性。 一、泛型的基本语法 在声明类、接口、方法时可以使用泛型泛型的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类型参数,多个类型参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个泛型类,`GenericInterface`是一个泛型接口,`genericMethod`是一个泛型方法。在这些声明中,`<T>`就是类型参数,可以用任何字母代替。 二、泛型的使用 1. 泛型类的使用 在使用泛型类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个泛型类,`<String>`指定了具体的类型参数,即`data`字段的类型为`String`,`gc`对象被创建时没有指定类型参数,因为编译器可以根据上下文自动推断出类型参数为`String`。 2. 泛型接口的使用 在使用泛型接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个泛型接口,`<String>`指定了具体的类型参数,匿名内部类实现了该接口,并使用`String`作为类型参数。 3. 泛型方法的使用 在使用泛型方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个泛型方法,`<T>`指定了类型参数,`T data`表示一个类型为`T`的参数,调用时可以传入任何类型的参数。 三、泛型的通配符 有时候,我们不知道泛型的具体类型,可以使用通配符`?`。通配符可以作为类型参数出现在方法的参数类型或返回类型中,但不能用于声明泛型类或泛型接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类型为`List<?>`,表示可以接受任何类型的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类型来遍历`List`中的元素。 四、泛型的继承 泛型类和泛型接口可以继承或实现其他泛型类或泛型接口,可以使用子类或实现类的类型参数来替换父类或接口的类型参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类型参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类型参数`T`。 五、泛型的限定 有时候,我们需要对泛型的类型参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类型参数的上限,或使用`super`关键字来限定类型参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类型参数`T`被限定为`Number`的子类,`GenericInterface`的类型参数`T`被限定为实现了`Comparable`接口的类。 六、泛型的擦除 在Java中,泛型信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取泛型的具体类型。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类型为`List<String>`,但是在运行时,`getClass`返回的类型为`java.util.ArrayList`,因为泛型信息已经被擦除了。 七、泛型的类型推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类型参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类型参数可以被编译器自动推断为`String`。 八、总结 Java泛型是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用泛型时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类型推断等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值