[Java]面向对象,从浅到深

快速入门

计算机的核心作用就是处理数据, 变量用来存储单个数据, 数组用来储存一批数据, 对象用来存储一类数据

什么是对象: 对象就是一种特殊的数据结构, 在java中万物皆对象

面相对象编程的好处: 更加符合人类思维习惯

类和实例对象

在java中必须先设计类, 才能根据类创建对象

类是创建对象的模板, 对象是类的具体实例

语法

成员变量

  1. 类中的变量称为成员变量, 类中的方法称为成员方法
  2. 完整定义格式:

  1. 一般无需指定初始值, 存在默认值

注意事项

  1. 类名称首字母大写, 满足驼峰模式
  2. 一个代码文件中, 可以写多个class类, 但只能有一个用public修饰, public修饰的类名必须是java代码的文件名
  3. 实际开发中, 建议一个文件只定义一个class类
执行原理

执行流程

  1. java程序运行在jvm虚拟机中
  2. jvm虚拟机把内存划分为3块, 方法区, 栈内存, 堆内存
  3. jvm虚拟机执行代码时, 首先把Test启动类提取到方法区中执行
  4. 然后把Test启动类的main方法放到栈内存中运行
  5. main方法中使用了Cart类, 虚拟机把Cart类提取到方法区
  6. new Cart时,会在堆内存中开辟空间, 存放汽车对象, 汽车象会有一个类的地址. 指向方法区的汽车类
  7. 存放汽车对象的变量地址会被赋值给左侧的c1变量, 对象创建完成
  8. 对象变量c1通过变量地址, 找到堆内存中的汽车对象, 在找到对象对象的name属性, 完成属性赋值

注意事项:

  1. 对象与对象之间的数据不会相互影响, 除非多个变量指向同一个对象
  2. 如果某个对象没有变量引用它, 该对象无法被操作,会被视为垃圾对象, 垃圾对象会被垃圾回收机制清除

构造器

构造器的作用: 定义在类中, 用于创建一个类的对象, 并返回对象的地址

public class Car {
    //成员变量
    private String  name;
    //无参构造器 
    public Car() {
    }
    //有参构造器
    public Car(String n) {
         this.name = n;
    }
}

// 调用无参构造器
Car c1 = new Car()
// 调用有参构造器
Car c2 = new Car("奔驰")
  1. 构造器必须与类名一致, 且没有返回值
  2. 无参构造器: 创建对象, 里面的数据都是默认值
  3. 有参构造器: 创建对象, 同时可以为里面的数据赋值
  4. 类在定义时, java会为类自动生成无参构造器
  5. 如果类中定义了有参构造器, java就不会自动生成无参构造器了, 此时建议手动补充无参构造器

this关键字

可以出现在构造器和成员方法中, 代表当前对象的地址

public class Car {
    // this在构造器中使用
    public Car() {
        sout("this1:" + this)
    }
    // this在成员方法中使用
    public void run() {
        sout("this2:" + this)
    }
}

作用

this关键字指向当前类的调用者, 用来访问该对象的成员变量和成员方法

解决对象成员变量与方法内部变量可能的命名冲突

三大特性-封装

概念

封装是一种设计对象的原则, 它帮助我们正确的设计对象的属性和方法

好处

让变成变得简单, 有什么事情, 找对象调方法就行了

原则

对象代表什么, 就要封装对应的数据, 并提供数据对应的行为

合理隐藏: 成员变量全部隐藏, 提供get/set方法访问数据

合理暴露: 成员方法根据是否有用对外暴露

修饰符

公开成员: public修饰符, 公开成员可以通过对象直接访问

私有成员: private修饰符, 私有成员无法通过对象访问, 只能在类的内部访问

public class Student {
    // 成员变量私有
    private double score;
    // 成员方法按需暴漏
    public void setScore(double score) {
        this.score = score;
    }
    // 通过方法操作数据
    public double getScore(double score) {
        return score;
    }
}

javaBean

javaBean也称为 实体类, 是一种专门保存数据的java类

要求

  1. 成员变量必须私有(private), 提供getXX/setXX方法
  2. 必须提供无参构造器, 有参构造器可选

好处

  1. 实际开发中数据和数据的处理都会很多, 如果都混在一起, 代码的可读性和可维护性都是很差
  2. 所以流行的开发方式就是数据和数据处理相分离, 所以要用实体类保存数据

static关键字

static是静态的意思, 也就是静态修饰符, 可以用来修饰成员变量, 成员方法

静态变量
// 定义
public class User {
    // 静态变量
    static String name;
    // 实例变量
    int age;
    
}
// 访问方式1(推荐)
// 通过类名访问: 类名.静态成员变量
User.name;

// 访问方式2(不推荐)
// 通过对象访问: 对象.静态成员变量
User u = new User();
// 访问静态变量
u.name;
// 访问实例变量
u.age;
  1. static修饰的成员变量就是静态变量(类变量)
  2. 静态变量属于类, 可以被类的所有对象访问
  3. 推荐通过类名访问静态变量, 因为用过对象访问实例变量, 并不直观
  4. 无static修饰的成员变量就是实例变量
  5. 实例变量是属于每个对象的
  6. 只能通过实例对象访问, 且每个对象的信息不同

特点

  1. 静态变量只会跟随类加载一次, 内存中只有一份
  2. 静态成员变量: 某个数据只需要一份, 且希望能够被共享, 该数据就应该定义为类变量
// 系统启动后, 需要用户类记住自己创建了多少个对象
public class User {
    // 类变量
    public static int number;
    // 构造器
    public User() {
        User.number++;
    }
}
静态方法
// 定义
public class User {
    // 实例方法
    public void run() {
        sout("好好学习.天天向上")
    }
    // 静态方法
    public static int getMax(int a, int b) {
        return a > b ? a : b
    }
    
}
// 访问方式1(推荐)
// 通过类名访问: 类名.静态成员方法
User.getMax();

// 访问方式2(不推荐)
// 通过对象访问: 对象.静态成员方法
User u = new User();
// 访问静态方法
u.getMax();
// 访问实例方法
u.run();
  1. 静态成员方法(static修饰, 属于类), 建议使用类名触发, 也可以用对象触发(不直观)
  2. 实例成员方法(无static修饰, 属于对象),只能用对象触发
  3. 如果表示对象自己的行为, 且方法中需要访问实例成员的, 则该方法必须声明成实例方法
  4. 如果该方法是执行公用功能, 则可以声明成静态方法
  5. 静态方法只能访问静态成员, 不可以直接访问实例成员
  6. 实例方法可以访问静态成员, 也可以访问实例成员
  7. 静态方法中不可以使用this关键字
main方法

main方法是类方法, 使用java命令执行Test(类)程序的时候, 虚拟机会通过Test(类)访问main方法, 然后运行

java Test -> Test.main()

工具类

工具类就是一个类中只定义类方法, 每个方法完成一个功能, 这个类就是工具类

好处

  1. 调用方便: 如果使用实例方法, 还要new出实例, 麻烦而且浪费内存
  2. 代码复用: 一次编写, 处处可用
  3. 建议: 工具类不需要创建对象, 建议把工具类的构造器进行私有
public class XxxUtil {
    // 方法1
    public static void xxx() {
        ...
    }
    // 方法2
    public static boolean xxx(String email) {
        ...
    }
    // 方法3
    public static String xxx(int n) {
        ...
    }
    // 构造器私有
    private XxxUtil() { }
}
代码块

代码块是类的5大成分之一: 成员变量/构造器/方法/代码块/内部类

在java中, 使用 {} 括起来的代码被称为代码块

静态代码块

  • 格式: static { }
  • 特点: 类加载时自动执行, 由于类只会加载一次, 所以静态代码块也只会执行一次
  • 使用: 在类加载的时候做一些静态数据初始化的操作, 例如: 对类变量的初始化赋值

实例代码块(了解)

  • 格式: { }
  • 特点: 每次创建对象, 调用构造器执行时, 都会执行实例代码块, 并在构造器之前执行
  • 使用: 和构造器一样, 用来完成对象的初始化赋值

三大特性-继承

继承就是用extends关键字, 让一个类和另一个建立父子关系

// Student称为子类
// People称为父类
public class Student extends People {
    
}
  1. 作用: 子类继承父类后, 就可以直接使用父类公共的属性和方法了
  2. 好处: 继承的好处就是提高代码的复用性

  1. 规范: 子类们相同的特性(公共属性/公共方法)放在父类中定义, 子类独有的属性和方法放在子类中定义
原理

子类的对象是由子类和父类共同决定的

权限修饰符

权限修饰符的作用就是 限制类中的成员能够被访问的范围

  • 访问权限从小到大是: private(私有)< 缺省(不写) < protected(受限制) < public(公开)
继承的特点
  1. 子类可以继承父类的属性和方法
  2. 子类可以继承父类的私有成员, 只是不能直接访问

  1. 子类不能继承父类的构造器, 因为子类有自己的构造器
  2. 子类可以直接使用父类的静态成员(共享), 但是共享不等于继承
  3. jaba是单继承模式: 一个类只能继承一个直接父类

  1. java不支持多继承, 但是支持多层继承

  1. java中所有的类都是Object类的子类, 要么直接继承, 要么间接继承
继承后的变化
1.子类访问成员的原则

在子类方法中访问成员(成员变量/成员方法)遵循就进原则

  • 先在子类局部范围找, 然后在子类成员范围找, 然后在父类范围找, 找不到就报错
  • 如果子父类中出现了重名的成员, 会优先使用子类的(更近)
  • 如果一定要在子类中使用父类的成员, 可以通过super关键字,指定访问父类的成员
  • 格式: super.父类成员变量/父类成员方法
  • 也可以通过this关键字, 指定访问子类的成员 (没用, 因为默认就访问子类的成员)
  • 格式: this.成员变量/成员方法
2.子类的方法重写

当子类觉得父类中的某个方法不好用, 子类可以重写一个名称和参数列表相同的方法, 去覆盖父类的方法

  1. 重写后, 方法的访问, java会遵循就近原则
  2. 建议使用@Override注解, 他可以让编译器检查重写的格式, 使代码可读性更好

基本要求

  1. 重写方法的名称和形参列表必须与被重写方法保持一致
  2. 私有方法, 静态方法不能被重写
  3. 子类重写父类方法, 访问权限必须等于或大于父类该方法的权限
  4. 重写的方法的返回值, 必须与被重写方法的返回值一致,或者范围更小
  5. 以上了解即可, 实际开中遵循: 声明不变, 重新实现

举例

  1. 可以通过重写Object类的toString方法, 使其返回对象内容, 而不是地址
  2. 可以通过右键->Generate->toString 快速生成
3.子类构造器的特点

子类中所有的构造器, 默认都会先调用父类的无参构造器, 再执行自己

原因

  • 子类在初始化的时候,有可能会用到父类中的数据, 如果父类没有初始化, 子类就无法使用父类的数据
  • 所以, 子类初始化之前, 一定会调用父类的构造器, 完成父类的初始化
  • 默认情况下, 子类构造器的第一行代码都是super() , 写不写都有, 他会调用父类的无参数构造器
  • 如果父类没有无参构造器, 则会报错, 我们需要在子类调用父类的有参构造器, super(参数)
  • 在继承模式下, 处理数据的构造器被分到多个不同的类中去了,
  • 所以需要先调用父类构造器, 再调用自己的构造器, 确保数据的完整初始化
4.子类其他构造器

如果父类中没有无参构造器, 代码就会报错, 因为子类默认要调用父类的无参构造器, 我们可以通过 super(...) 调用父类的有参构造器, 解决问题

// 父类
public class People {
    private String name;
    // 有参构造器 
    public Prople(String name) {
        this.name = name   
    }
}

// 子类
public class Student extends People {
   private int age;
    // 有参构造器
   public Student(int age) {
     // 调用兄弟构造器 
     this(age, '张三');
   }
   // 有参构造器
   public Student(int age, String name) {
       // 调用父类有参构造器
       super(name)
       // 初始化对象
       this.age = age;
   }
}

// 初始化学生对象
Student s = new Student(18)

注意

  • 子类通过 this(...) 调用本类的其他构造器, 其他构造器可以通过 super(...) 去调用父类的构造器
  • super(...) 和 this(...) 都只能放在构造器的第一行, 所以不能同时使用
this和super

this代表本类对象的引用, super代表父类存储空间的标识

包就是分门别类的管理类的, 类似于文件夹

建包

// 语句
// package 公司域名倒写.技术名称
// 必须在第一行, 一般IEDA工具会帮助创建
package com.itheima.javabean;
public class Student {
    
}

导包

相同包下的类可以直接访问, 不同包下的类必须导包

  1. 语句: import 包名.类名;
  2. 使用java官方的程序, 也需要导包, lang包下的程序可以直接用
  3. 一个类中使用多个不同包下的类, 如果类的名字一样, 默认只能导入一个, 另一个必须带包名访问

自动导包

权限修饰符

用来控制一个成员能够被访问的范围, 可以修饰成员变量, 构造器, 内部类

  1. 能够识别他人定义的成员的访问范围
  2. 自己定义成员一般要满足如下要求:
  • 成员变量一般私有
  • 方法一般公开
  • 希望该成员只能被本类访问, 使用private修饰
  • 希望该成员只能被本类和同一个包下的其他类及子类访问, 使用protected修饰
  • 其他情况用public就行

final关键字

final关键字是最终的意思, 可以修饰类/方法/变量

  1. 修饰类: 该类就是最终类, 特点是不能被继承, 工具类中可能会用到
  2. 修饰方法: 该方法就是最终方法, 特点是不能被重写
  3. 修饰变量: 该变量就只能被赋值一次,不能再次赋值
  • 基本类型的变量: 该变量存储的数据不能被改变,
  • 引用类型的变量: 该变量存储的地址不能被改变, 但是对象里面的值可以改变
常量

使用public static final 修饰的成员变量就是常量, 必须要有初始值

  1. 命名规范: 全大写英文, 使用下划线连接
  2. 作用: 通常用于记录系统的配置信息

  1. 优势:
  • 实现软编码, 代码可读性更好,可维护性更好
  • 程序编译后,常量会被"宏替换", 即出现常量的地方全部会被替换成其记住的字面量,保证常量和字面量的性能是一样的

枚举

枚举是java中一种特殊的类, 专门用来做信息分类, 可读性好, 入参约束谨慎, 代码优雅

定义

public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
}

反编译后

  1. 枚举类都是继承了枚举类型: java.lang.Enum
  2. 枚举都是最终类, 不可以被继承
  3. 枚举类的构造器都是私有的, 枚举对外不能创建对象
  4. 枚举类的第一行只 能罗列一些名称, 这些名称都是常量, 并且每个常量记住的都是枚举类的一个对象
  5. 枚举类中, 从第二行开始, 可以定义类的其他各种成员
  6. 枚举类相当于是多例模式
  7. 枚举会从 java.lang.Enum 类中继承一些方法
  • Season[] se = Season.values() // 拿到全部对象
  • Season se = Season.valueOf("SPRING"); // 拿到指定名称的枚举对象
  • se.name() // 拿到枚举对象的名字
  • se.ordinal() // 拿到枚举对象的索引

补充

  1. 可以使用枚举实现单例模式
public enum C {
     x;  //单例
}
  1. 枚举和常量都是用来表示一组信息, 然后作为参数进行传输
  • 枚举相对于常量, 限制性更好,
  • 常量相对于枚举, 灵活性更好,
  • 两者的使用场景相似

抽象类

abstract关键字是抽象的意思, 可以修饰类, 成员方法

// 抽象类
修饰符 abstract class 类名 {  
    // 抽象方法
    修饰符 abstract 返回值类型 方法名称(形参列表);
}
  1. 抽象方法只有方法签名, 不能声明方法体
  2. 一个类中如果定义了抽象方法,这个类必须声明为抽象类, 否则报错
  3. abstract修饰的类称为抽象类, abstract修饰的方法称为抽象方法
  4. 类该有的成员(成员变量/方法/构造器),抽象类都可以有
  5. 抽象类可以理解为不完整的设计图, 得到了抽象方法, 但是失去了创建对象的能力
  6. 所以抽象类不能创建对象, 仅作为一种特殊的类, 让子类继承并实现抽象方法
  7. 一个类继承抽象类, 必须重写完抽象类的全部抽象方法, 否则这个类也要定义成抽象类
  8. abstract不能修饰变量, 代码块, 构造器

示例

父类知道每个子类都要做某个行为, 但每个子类要做的情况不一样

  1. 父类就可以定义抽象方法, 交给子类去重写实现,
  2. 设计这样的抽象类, 就是为了更好的支持多态

扩展

  1. final和abstract是互斥关系
  2. abstract定义的抽象类作为模板让子类继承, final定义的类不能被继承
  3. 抽象方法定义通用功能让子类重写, final定义的方法子类不能重写
  4. 使用抽象类可以实现 [模版方法] 设计模式

三大特性-多态

多态就是对象可以有多种形态, 是继承/实现情况下的一种现象, 表现为:对象多态和行为多态

对象多态: 一个对象可以有角色(形态), 你既是你爸爸的儿子, 也是你媳妇的老公

行为多态: 一个对象的行为可以有不同的表现, 人都会唱歌, 但是你唱的和刘德华唱的不一样

注意: 多态是对象或行为的多态, java中的属性(成员变量)不谈多态

多态的前提
  1. 有继承/实现关系;
  2. 存在父类引用子类对象;
  3. 存在方法重写 (多态侧重行为多态)
常见形式

父类类型 对象名称 = new 子类构造器

public class Prople {
    public String name = "父类的名称"
    public void run() {
        sout("人可以跑")
    }
}
public class Teacher extends Prople {
    public String name = "老师的名称"
    public void run() {
        sout("老师跑的气喘吁吁")
    }
}
public class Student extends Prople {
    public String name = "学生的名称"
    public void run() {
        sout("学生跑的飞快")
    }
}
public class Test {
    public static void main(String[] ages) {
        // 对象多态: 人对象可以指向学生对象,也可以指向老师对象
        People p1 = new Student();
        People p2 = new Teacher();
        
        // 行为多态: 同样调用run方法,执行结果不同
        p1.run(); // 学生跑的飞快
        p2.run(); // 老师跑的气喘吁吁
        
        // 成员变量不存在多态
        p1.name;  // 父类的名称
        p1.name;  // 父类的名称

    }
}

运行特点

  1. 方法调用: 编译看左边(People), 运行看右边(Student();)
  2. 变量调用: 编译看左边(People), 运行也看左边(People)
多态的好处

1:在多态形势下, 右边对象是解耦合的, 更便于扩展和维护

// 可以方便的切换
// prople p1 = new Teacher(); 
prople p1 = new Student();
p1.run()

2:定义方法时, 使用父类类型的形参, 可以接受一切子类对象, 扩展性更强

Student s = new Student();
go(s);

Teacher t = new Teacher();
go(t);

publci static void go(People p) {
    
}
多态的问题

多态下不能使用子类的独有功能

public class Prople {
    public void run() {
        sout("人可以跑")
    }
}
public class Teacher extends Prople {
    public void run() {
        sout("老师跑的气喘吁吁")
    }
    public void sayHi() {
        sout("老师需要讲课")
    }
}
public class Test {
    public static void main(String[] ages) {
        People p1 = new Student();
        p1.run();   // 老师跑的气喘吁吁
        p1.sayHi(); // 报错 
    }
}

解决方法

强制类型转换: 把父类类型强转成子类类型, 解决多态下无法使用子类独有功能的问题

方法: 子类 变量名 = (子类) 父类变量

public class Test {
    public static void main(String[] ages) {
        People p1 = new Student();
        // 强制类型转换
        Student s1 = (Student) p1;
        
        // 正常运行: "老师需要讲课"
        p1.sayHi();
        
        // 类型谈判, 防止类型转换错误
        if(p1 instanceof Student) {
             Student s1 = (Student) p1;
             p1.sayHi();
        }
        
    }
}

补充

  1. 只要存在继承/实现关系, 就可以在编译阶段进行强制类型转换, 编译阶段不会报错
  2. 运行时, 如果发现强转的类型和对象的真实类型不符, 就会报类型转换异常(ClassCastException)的错误
  3. 官方建议, 强制类型转换前先 使用 instanceof 关键字, 判断当前对象的真实类型

接口

接口是一种规范, 在java中, 使用 interface关键字定义接口, 可以理解为一种特殊的结构

定义
public interface A {
     // 成员变量(默认就是常量)
     String SCHOOL_NAME = "程序员";
     //  成员方法(默认就是抽象方法)
     void test();
}
  1. JDK8之前的接口中只能有抽象方法和常量
  2. 接口中的成员都是public修饰的, 写不写都是
  3. 接口方法默认被public修饰的原因: 接口是需要被类实现的, 大部分接口方法都是要对外暴漏的
  4. 接口不能实例化
使用

接口不能创建对象, 接口是用来被 类 实现的, 实现接口的类称为实现类

// 实现接口的关键字: implements
修饰符 class 实现类 implements 接口1, 接口2, ... {
    
}
  1. 一个类可以实现多个接口(干爹)
  2. 一个类实现接口, 必须重写完 全部接口的 全部抽象方法, 否则实现类需要定义为抽象类
优势
  1. 弥补了类单继承的不足,一个类可以同时实现多个接口
  2. 面向接口编程, 可以灵活方便的切换各种业务实现
新增

jdk8开始, 接口新增了三种方法, 目的是增强接口的能力, 更便于项目的扩展和维护

了解即可, java源码中会看到

1.默认方法

// 默认方法必须使用default修饰
// 默认方法, 使用实现类的对象调用
default void run() {
    sout("默认方法")
}

2.静态方法

// 静态方法必须使用static修饰
// 静态方法, 使用本身的接口名来调用
static void run() {
    sout("静态方法")
}

3.私有方法

// 私有方法必须使用private修饰
// jdk9开始支持, 只能在本类中被其他的默认方法或私有方法访问
static void run() {
    sout("私有方法")
}

接口的多继承

一个接口可以同时继承多个接口, 便于实现类实现多个接口

public interface 接口1 extends 接口1, 接口2 {
    
}
接口使用细节

一个接口继承多个接口, 如果多个接口中存在方法签名冲突, 则此时不支持多继承

一个类实现多个接口, 如果多个接口中存在方法签名冲突, 则此时不支持多实现

一个类继承了父类, 又同时实现了接口, 父类中和接口中存在同名的默认方法, 实现类会优先使用父类的

一个类实现了多个接口, 多个接口中存在同名的默认方法, 会冲突, 解决方法就是在实现类中重写该方法

内部类

如果一个类中定义在另一个类的内部, 这个类就是内部类

内部类是类的五大成分之一(成员变量/方法/构造器/内部类/代码块)

public class People {
    // 内部类
    public class Heart {
        
    }
}
  1. 场景: 当一个类的内部, 包含了一个完成的事物, 且这个事物没必要单独设计时, 就可以设计成内部类
  2. 内部类通常可以方便访问外部类的成员, 包括私有成员
  3. 内部类提供了更好的封装性, 可以使用private, protectecd修饰内部类
静态内部类

用static修饰的内部类, 属于外部类自己持有, 特点与普通类一致

// 定义
public class Car {
   // 静态成员内部类
   public static class Engine { }
}

// 创建对象
Car.Engine eg = new Car.Engine();

// 访问成员
// 可以直接访问外部类的静态成员
// 不可以直接访问外部类的实例成员
成员内部类

无static修饰, 属于外部类的对象, JDK16之后, 成员内部类也可以定义静态成员了

// 定义
public class Car {
    // 成员内部类
   public class Engine { }
}

// 创建对象
Car.Engine  eg = new Car().new Engine();

// 访问成员
// 可以直接访问外部类的实例成员和静态成员
// 可以拿到当前外部类对象, 格式是: 外部类名.this
匿名内部类(重点)

本质是一个没有名字的局部内部类, 作用就是方便的创建一个子类对象, 目的是简化代码

// 1.定义一个抽象类
abstract class Animal {
    public abstract void cry();
}

// 2.使用匿名内部类语法
// 把匿名内部类编译成一个子类, 然后立即创建一个子类对象
Animal a = new Animal(){
    @Override
    public void cry() {
        sout("喵喵喵")
    }
};

// 直接使用子类 
a.run(); // 喵喵喵
  1. 语法:

  1. 匿名内部类本质是一个子类, 并且会立即创建一个子类对象
  2. 匿名内部类的对象类型, 相当于是当前new的那个类型子类类型
  3. 使用场景: 把匿名内部类当做参数传给方法
  4. 匿名内部类都是在需要的时候用, 比如一个方法需要一个对象作为参数
  5. 那么单独创建对象传进去就麻烦, 此时可以使用匿名内部类简化代码

泛型

泛型提供了在编译阶段的类型约束, 并自动进行检查, 可以避免强制类型转换和可能出现的转换异常

// 该集合只能添加字符串类型的数据
ArrayList<String> list1 = new ArrayList<>();
list1.add("java1");
  1. 定义类,接口,方法时, 同时声明一个或多个类型变量,用于限制成员类型,
  2. 使用泛型的类称为泛型类, 泛型接口或泛型方法, 统称为泛型
  3. 原理: 把具体的数据类型作为参数传给类型变量, 通过类型变量限制成员的类型
自定义泛型类
// 模拟ArrayList集合
public class MyArrayList<E> {
    // 模拟add方法
    public boolean add(E e) {
          return true;
     }
    // 模拟get方法
    public E get(int index) {
          return null;
    }
}



// 使用泛型
MyArrayList<String> list = new MyArrayList<>();
list.add("java1")
list.get(1)
  1. 如果需要, 可以进一步使用extends限制泛型的范围

public class MyArrayList<E extends Animal> { }

  1. 表示类型E必须是Animal类型或者是Animal的子类
自定义泛型接口
public interface Date<T> {
    void add(T t);
    ArrayList<T> getByName(String name):
}
  1. 类型变量建议使用大写英文字母, E T K V 等
自定义泛型方法
public static <T> T test(T t) {
     return t;
}
通配符
  1. 在使用泛型是可以用 ? 表示一切类型
泛型上下限
  1. 泛型上限: ? extends Cart
  2. 表示能接受的类型必须是Car或其子类
  1. 泛型下限: ? super Cart
  2. 表示能接受的必须是Cart或其父类
泛型擦除
  1. 泛型是在编译阶段工作的. 就是帮助程序进行类型校验的, 程序编译成class文件后, 泛型就不会存在了
  2. 泛型不支持基本数据类型, 只支持引用数据对象, 如Integer Double
  • 49
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值