文章目录
Java基础(2) 之面向对象
-
面向对象编程有什么好处?
-
面向对象的开发更符合人类的思维习惯,让编程变得更加简单、更加直观。
1.对象
-
对象实质上是一种特殊的数据结构。这种结构怎么理解呢?
你可以把对象理解成一张表格,表当中记录的数据,就是对象拥有的数据
一句话总结,对象其实就是一张数据表,表当中记录什么数据,对象就处理什么数据。
-
Student s1 = new Student();这句话中的原理如下-
Student s1表示的是在栈内存中,创建了一个Student类型的变量,变量名为s1 -
而
new Student()会在堆内存中创建一个对象,而对象中包含学生的属性名和属性值同时系统会为这个Student对象分配一个地址值
0x4f3f5b24 -
接着把对象的地址赋值给栈内存中的变量
s1,通过s1记录的地址就可以找到这个对象 -
当执行
s1.name=“播妞”时,其实就是通过s1找到对象的地址,再通过对象找到对象的name属性,再给对象的name属性赋值为播妞;
-
2.类
用什么来设计这张表呢?就是类(class),类可以理解成对象的设计图,或者对象的模板。设计图中规定有哪些数据,对象中就只能有哪些数据。
- 对象可以理解成一张数据表,而数据表中可以有哪些数据,是有类来设计的。
类的注意事项

3.this关键字
- 哪一个对象调用方法,方法中的this就是哪一个对象
通过this在方法中可以访问本类对象的成员变量
4.构造器
构造器就是用来创建对象的。可以在创建对象时给对象的属性做一些初始化操作
-
构造器其实是一种特殊的方法,但是这个方法没有返回值类型,方法名必须和类名相同。
-
在创建对象时,会调用构造器。
-
也就是说
new Student()就是在执行构造器,当构造器执行完了,也就意味着对象创建成功。 -
new 对象就是在执行构造方法
注意
- 1.在设计一个类时,如果
不写构造器,Java会自动生成一个无参数构造器。 - 2.一旦
定义了有参数构造器,Java就不再提供空参数构造器,此时建议自己加一个无参数构造器。
5.封装性
所谓封装,就是用类设计对象处理某一个事物的数据时,应该把要处理的数据,以及处理数据的方法,都设计到一个对象中去。
-
封装的设计规范:
- 合理隐藏、合理暴露
-
体现:
-
被
private修饰的变量或者方法,只能在本类中被访问。 -
private double score;就相当于把score变量封装在了Student对象的内部,且不对外暴露,你想要在其他类中访问score这个变量就,就不能直接访问了;如果你想给Student对象的score属性赋值,得调用对外暴露的方法
setScore(int score),在这个方法中可以对调用者传递过来的数据进行一些控制,更加安全。
-
6.实体JavaBean
实体类
- 实体类中除了有给对象存、取值的方法就没有提供其他方法了。所以实体类仅仅只是用来封装数据用的。
实体类就是一种特殊的类,它需要满足下面的要求:
- 实体类仅仅只用来
封装数据,而对数据的处理交给其他类来完成,以实现数据和数据业务处理相分离。
7.成员变量和局部变量的区别

面向对象的核心点就是封装,将数据和数据的处理方式,都封装到对象中; 至于对象要封装哪些数据?对数据进行怎样的处理? 需要通过类来设计。
需要注意的是,不同的人,对同一个对象进行设计,对象封装那些数据,提供哪些方法,可能会有所不同;只要能够完成需求,符合设计规范,都是合理的设计。
8.static
static读作静态,可以用来修饰成员变量,也能修饰成员方法。
static修饰成员变量
- 实际开发中,如果某个数据只需要一份,且希望能够被
共享(访问、修改),则该数据可以定义成类变量来记住。
Java中的成员变量按照有无static修饰分为两种:类变量、实例变量。它们的区别如下:
静态变量是属于类的,只需要通过类名就可以调用:
类名.静态变量实例变量是属于对象的,需要通过对象才能调用:
对象.实例变量
static修饰成员方法
成员方法根据有无static也分为两类:类方法、实例方法
有static修饰的方法,是属于类的,称为类方法;调用时直接用类名调用即可。
无static修饰的方法,是属于对象的,称为
实例方法;调用时,需要使用对象调用。
static的注意事项

工具类
如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个
工具
- 工具类里的方法全都是静态的,推荐用类名调用为了
防止使用者用对象调用。我们可以把工具类的构造方法私有化
public class MyUtils{
//私有化构造方法:这样别人就不能使用构造方法new对象了
private MyUtils(){
}
//类方法
public static String createCode(int n){
...
}
}
-
示例:生成验证码的工具类
-
public class MyUtils{ public static String createCode(int n){ //1.定义一个字符串,用来记录产生的验证码 String code = ""; //2.验证码是由所有的大写字母、小写字母或者数字字符组成 //这里先把所有的字符写成一个字符串,一会从字符串中随机找字符 String data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKMNOPQRSTUVWXYZ"; //3.循环n次,产生n个索引,再通过索引获取字符 Random r = new Random(); for(int i=0; i<n; i++){ int index = r.nextInt(data.length()); char ch = data.charAt(index); //4.把获取到的字符,拼接到code验证码字符串上。 code+=ch; } //最后返回code,code的值就是验证码 return code; } }
单例设计模式
让类对外只能产生一个对象;如任务管理器对象
-
把类的构造器私有。
-
定义一个类变量记住类的一个对象。
-
定义一个类方法,返回对象。
-

-
懒汉式(拿对象时,才开始创建对象。)
-
把类的构造器私有。
定义一个类变量用于存储对象。
提供一个类方法,保证返回的是同一个对象。

-
9.代码块
代码块根据有无static修饰分为两种:
静态代码块、实例代码块
静态代码块

静态代码块不需要创建对象就能够执行
静态代码块,随着类的加载而执行,而且只执行
一次。
实例代码块
-
实例代码块的作用和构造器的作用是一样的,用来给对象初始化值;而且每次创建对象之前都会先执行实例代码块.

10.继承

子类对象实际上是由子、父类两张设计图共同创建出来的。
其实像这种两个类中有相同代码时,没必要重复写。
我们可以把重复的代码提取出来,作为父类,然后让其他类继承父类就可以了,这样可以提高代码的复用性。
权限修饰符
权限修饰符是用来限制类的成员(成员变量、成员方法、构造器…)能够被访问的范围。
单继承与object
-
Java语言只支持单继承,不支持多继承,但是可以多层继承。
-
Java是
单继承的:一个类只能继承一个直接父类; -
Object类是Java中所有类的祖宗。
方法重写
当子类觉得父类方法不好用,或者无法满足父类需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
-
重写后,方法的访问遵循
就近原则-
1.重写的方法上面,可以加一个注解@Override,用于标注这个方法是复写的父类方法
-
2.子类复写父类方法时,访问权限必须大于或者等于父类方法的权限
-
public > protected > 缺省
-
-
- 重写的方法返回值类型,必须与被重写的方法返回值类型一样,或者范围更小
- 私有方法、静态方法不能被重写,如果重写会报错。
-
-
示例:
-
public class A { public void print1(){ System.out.println("111"); } public void print2(int a, int b){ System.out.println("111111"); } } -
public class B extends A{ // 方法重写 @Override // 安全,可读性好 public void print1(){ System.out.println("666"); } // 方法重写 @Override public void print2(int a, int b){ System.out.println("666666"); } } -
尽量做到
声明不变,重新实现
-
应用场景之一就是:子类重写Object的toString()方法,以便返回对象的内容。
子类访问成员的特点
继承至少涉及到两个类,而每一个类中都可能有各自的成员(成员变量、成员方法),就有可能出现子类和父类有相同成员的情况,那么在子类中访问其他成员有什么特点呢?
-
在子类中访问其他成员(成员变量、成员方法),是依据就近原则的
-
如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加
this或者super进行区分。
子类访问构造器的特点
子类中访问构造器的语法规则
- 子类全部构造器,都会
先调用父类构造器,再执行自己。
- 如果不想使用默认的
super()方式调用父类构造器,还可以手动使用super(参数)调用父类有参数构造器。
访问自己类的构造器
this(): 调用本类的空参数构造器
this(参数): 调用本类有参数的构造器
如下图:
- 注意:this和super访问构造方法,只能用到
构造方法的第一句,否则会报错。
访问本类成员:
this.成员变量 //访问本类成员变量
this.成员方法 //调用本类成员方法
this() //调用本类空参数构造器
this(参数) //调用本类有参数构造器访问父类成员:
super.成员变量 //访问父类成员变量
super.成员方法 //调用父类成员方法
super() //调用父类空参数构造器
super(参数) //调用父类有参数构造器
11.多态
什么是多态?
多态是在继承、实现情况下的一种现象,表现为:对象多态、行为多态。

在多态形式下,右边的代码是解耦合的,更便于扩展和维护。
定义方法时,使用父类类型作为形参,可以接收一切子类对象,扩展行更强,更便利。
public class Test2 { public static void main(String[] args) { // 目标:掌握使用多态的好处 Teacher t = new Teacher(); go(t); Student s = new Student(); go(s); } //参数People p既可以接收Student对象,也能接收Teacher对象。 public static void go(People p){ System.out.println("开始------------------------"); p.run(); System.out.println("结束------------------------"); } }
多态的类型转换
多态形式下,不能调用
子类特有的方法,比如在Teacher类中多了一个teach方法,在Student类中多了一个study方法,这两个方法在多态形式下是不能直接调用的。
- 但是转型后是可以调用的
- 因此我们最终记住一句话:原本是什么类型,才能还原成什么类型
12.final关键字
面向对象编程中偶尔会用到的一个关键字叫
final,也是为后面学习抽象类和接口做准备的
-
final修饰
类:该类称为最终类,特点是不能被继承 -
-
final修饰
方法:该方法称之为最终方法,特点是不能被重写。 -
final修饰
变量:该变量只能被赋值一次。 -
-
static final 修饰的成员变量,称之为
常量。 -
在程序编译后,
常量会“宏替换”,出现常量的地方,全都会被替换为其记住的字面量。
13.抽象类
在Java中有一个关键字叫abstract,它就是抽象的意思,它可以修饰
类也可以修饰方法。
- 被abstract修饰的类,就是抽象类
- 被abstract修饰的方法,就是抽象方法(
不允许有方法体)
//abstract修饰类,这个类就是抽象类
public abstract class A{
//abstract修饰方法,这个方法就是抽象方法
public abstract void test();
}
- 类的成员(成员变量、成员方法、构造器),类的成员都可以有。
- 抽象类是不能创建对象的,如果抽象类的对象就会报错
抽象类虽然不能创建对象,但是它可以作为父类让子类继承。而且子类继承父类必须重写父类的所有抽象方法。
子类继承父类如果不复写父类的抽象方法,要想不出错,这个子类也必须是抽象类
-
好处
-
1.用抽象类可以把父类中相同的代码,包括方法声明都抽取到父类,这样能更好的支持多态,一提高代码的灵活性。
2.反过来用,我们不知道系统未来具体的业务实现时,我们可以先定义抽象类,将来
让子类去实现,以方便系统的扩展。
-
模板方法模式
模板方法模式解决了多个子类中有相同代码的问题
第1步:定义一个抽象类,把子类中相同的代码写成一个模板方法。
第2步:把模板方法中不能确定的代码写成抽象方法,并在模板方法中调用。
第3步:子类继承抽象类,只需要父类抽象方法就可以了。
-
模板方法模式主要解决方法中存在重复代码的问题
-

-
我们可以写一个抽象类C类,在C类中写一个doSing()的抽象方法。再写一个sing()方法
先设计模板方法:
// 模板方法设计模式 public abstract class C { // 模板方法 public final void sing(){ System.out.println("唱一首你喜欢的歌:"); doSing(); System.out.println("唱完了!"); } public abstract void doSing(); } -
写一个A类和B类继承C类,复写doSing()方法,代码如下
-
public class A extends C{ @Override public void doSing() { System.out.println("我是一只小小小小鸟,想要飞就能飞的高~~~"); } }public class B extends C{ @Override public void doSing() { System.out.println("我们一起学猫叫,喵喵喵喵喵喵喵~~"); } }
-
14.接口
-
总结为一句话,就是接口实现之后我就可以用接口来接收所有实现了接口的对象,因为所有的这些对象都
重写了接口的方法,充分利用了多态。
比抽象类抽象得更加彻底的一种特殊结构
-
Java提供了一个关键字
interface,用这个关键字来定义接口这种特殊结构。格式如下: -
public interface 接口名{ //成员变量(常量) //成员方法(抽象方法) }定义好接口之后,是不能创建对象的
-
所以说接口有什么用?
-
- 接口是用来被类实现(implements)的,我们称之为实现类。
- 一个类是可以实现多个接口的(接口可以理解成干爹),类实现接口必须重写所有接口的全部抽象方法,否则这个类也必须是抽象类
-
比如,定义一个B接口,里面有两个方法testb1(),testb2()
-
public interface B { void testb1(); void testb2(); } -
再定义一个C接口,里面有两个方法testc1(), testc2()
-
public interface C { void testc1(); void testc2(); } -
再写一个实现类D,同时实现B接口和C接口,此时就需要复写
四个方法 -
// 实现类 public class D implements B, C{ @Override public void testb1() { } @Override public void testb2() { } @Override public void testc1() { } @Override public void testc2() { } }
好处:
弥补了类单继承的不足,一个类同时可以实现多个接口。
让程序可以面向接口编程,这样程序员可以
灵活方便的切换各种业务实现。class Student{ } interface Driver{ void drive(); } interface Singer{ void sing(); } //A类是Student的子类,同时也实现了Dirver接口和Singer接口 class A extends Student implements Driver, Singer{ @Override public void drive() { } @Override public void sing() { } } public class Test { public static void main(String[] args) { //想唱歌的时候,A类对象就表现为Singer类型 Singer s = new A(); s.sing(); //想开车的时候,A类对象就表现为Driver类型 Driver d = new A(); d.drive(); } }接口弥补了单继承的不足,同时可以轻松实现在多种业务场景之间的
切换。
-
在JDK8版本以后接口中能够
定义的成员也做了一些更新-
public interface A { /** * 1、默认方法:必须使用default修饰,默认会被public修饰 * 实例方法:对象的方法,必须使用实现类的对象来访问。 */ default void test1(){ System.out.println("===默认方法=="); test2(); } /** * 2、私有方法:必须使用private修饰。(JDK 9开始才支持的) * 实例方法:对象的方法。 */ private void test2(){ System.out.println("===私有方法=="); } /** * 3、静态方法:必须使用static修饰,默认会被public修饰 */ static void test3(){ System.out.println("==静态方法=="); } void test4(); void test5(); default void test6(){ } }- 我们写一个B类,实现A接口。B类作为A接口的实现类,只需要重写抽象方法就尅了,对于默认方法不需要子类重写。
public class B implements A{ @Override public void test4() { } @Override public void test5() { } }public class Test { public static void main(String[] args) { // 目标:掌握接口新增的三种方法形式 B b = new B(); b.test1(); //默认方法使用对象调用 // b.test2(); //A接口中的私有方法,B类调用不了 A.test3(); //静态方法,使用接口名调用 } }就是说jdk8以后接口中的方法可以被
default,static,private修饰,这些方法可以在接口中实现,而什么都不加,就不能实现,得实体类来实现。感觉开始和抽象类差不多了。
-
接口也能继承
-
一个接口可以继承多个接口,接口同时也可以被类实现。
-
1.一个接口继承多个接口,如果多个接口中存在相同的方法声明,则此时不支持多继承
2.一个类实现多个接口,如果多个接口中存在相同的方法声明,则此时不支持多实现
3.一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会有限使用父类的方法
4.一个类实现类多个接口,多个接口中有同名的默认方法,则这个类必须重写该方法。
public class Test { public static void main(String[] args) { // 目标:理解接口的多继承。 } } interface A{ void test1(); } interface B{ void test2(); } interface C{} //比如:D接口继承C、B、A interface D extends C, B, A{ } //E类在实现D接口时,必须重写D接口、以及其父类中的所有抽象方法。 class E implements D{ @Override public void test1() { } @Override public void test2() { } }
15.内部类
- 当一个类的内部,包含一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类
内部类是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
成员内部类
- 既可以访问内部类成员、也可以访问外部类成员
- 如果内部类成员和外部类成员同名,可以使用**
类名.this.成员**区分
成员内部类就是类中的一个普通成员,类似于成员变量、成员方法。
-
public class Outer { private int age = 99; public static String a="黑马"; // 成员内部类 public class Inner{ private String name; private int age = 88; //在内部类中既可以访问自己类的成员,也可以访问外部类的成员 public void test(){ System.out.println(age); //88 System.out.println(a); //黑马 int age = 77; System.out.println(age); //77 System.out.println(this.age); //88 System.out.println(Outer.this.age); //99 } } }成员内部类如何创建对象,格式如下:
//外部类.内部类 变量名 = new 外部类().new 内部类(); Outer.Inner in = new Outer().new Inner(); //调用内部类的方法 in.test();
静态内部类
在成员内部类的前面加了一个static关键字。静态内部类属于外部类自己持有。
-
public class Outer { private int age = 99; public static String schoolName="黑马"; // 静态内部类 public static class Inner{ //静态内部类访问外部类的静态变量,是可以的; //静态内部类访问外部类的实例变量,是不行的 public void test(){ System.out.println(schoolName); //99 //System.out.println(age); //报错 } } }静态内部类创建对象时,需要使用外部类的类名调用。
//格式:外部类.内部类 变量名 = new 外部类.内部类(); Outer.Inner in = new Outer.Inner(); in.test();
局部内部类
局部内部类是定义在方法中的类,和局部变量一样,
只能在方法中有效。所以局部内部类的局限性很强,一般在开发中是不会使用的
public class Outer{ public void test(){ //局部内部类 class Inner{ public void show(){ System.out.println("Inner...show"); } } //局部内部类只能在方法中创建对象,并使用 Inner in = new Inner(); in.show(); } }
匿名内部类
匿名内部类是一种特殊的
局部内部类;所谓匿名,指的是程序员不需要为这个类声明名字。匿名内部类本质上是一个没有名字的子类对象、或者接口的实现类对象。
匿名内部类的作用:简化了创建子类对象、实现类对象的书写格式。
new 父类/接口(参数值){ @Override 重写父类/接口的方法; }
-
示例:
-
public abstract class Animal{ public abstract void cry(); }我想要在
不定义子类的情况下创建Animal的子类对象,就可以使用匿名内部类public class Test{ public static void main(String[] args){ //这里后面new 的部分,其实就是一个Animal的子类对象 //这里隐含的有多态的特性: Animal a = Animal子类对象; Animal a = new Animal(){ @Override public void cry(){ System.out.println("猫喵喵喵的叫~~~"); } } a.eat(); //直线上面重写的cry()方法 } }- 匿名内部类在编写代码时没有名字,编译后系统会为
自动为匿名内部类生产字节码,字节码的名称会以外部类$1.class的方法命名
- 匿名内部类在编写代码时没有名字,编译后系统会为
-
**只有在调用方法时,当方法的形参是一个接口或者抽象类,为了简化代码书写,而直接传递匿名内部类对象给方法。**这样就可以少写一个类。
-
public interface Swimming{ public void swim(); }public class Test{ public static void main(String[] args){ Swimming s1 = new Swimming(){ public void swim(){ System.out.println("狗刨飞快"); } }; go(s1); Swimming s1 = new Swimming(){ public void swim(){ System.out.println("猴子游泳也还行"); } }; go(s1); } //形参是Swimming接口,实参可以接收任意Swimming接口的实现类对象 public static void go(Swimming s){ System.out.println("开始~~~~~~~~"); s.swim(); System.out.println("结束~~~~~~~~"); } }
-
16.枚举
枚举是一种特殊的类,它的格式是:
public enum 枚举类名{ 枚举项1,枚举项2,枚举项3; }枚举项就表示枚举类的对象,只是这些对象在定义枚举类时就预先写好了,以后就只能用这几个固定的对象。
枚举项实际上是枚举类的对象
枚举类A是用class定义的,说明枚举确实是一个类,而且X,Y,Z都是A类的对象;而且每一个枚举项都是被
public static final修饰,所以被可以类名调用,而且不能更改。
-
既然枚举是一个类的话,我们能不能在枚举类中
定义构造器、成员变量、成员方法呢?答案是可以的-
public enum A{ //定义枚举项 X,Y,Z("张三"); //枚举项后面加括号,就是在执行枚举类的带参数构造方法。 //定义空构造器 public A(){ } //成员变量 private String name; //定义带参数构造器 public A(String name){ this.name=name; } //成员方法 public String getName(){ return name; } ... }虽然枚举类中可以像类一样,写一些类的其他成员,但是一般不会这么写,如果你真要这么干的话,到不如直接写普通类来的直接。
-
-
枚举一般表示几个固定的值,然后作为参数进行传输。
public enum Constant{ BOY,GRIL }public class Test{ public static void main(String[] args){ //调用方法,传递男生 provideInfo(Constant.BOY); } public static void provideInfo(Constant c){ switch(c){ case BOY: System.out.println("展示一些信息给男生看"); break; case GRIL: System.out.println("展示一些信息给女生看"); break; } } }
17.泛型
泛型类
- 泛型类,在实际工作中一般都是源代码中写好,我们直接用的,就是
ArrayList<E>这样的,自己定义泛型类是非常少的。
所谓泛型指的是,在定义类、接口、方法时,同时声明了一个或者多个类型变量(如:),称为泛型类、泛型接口、泛型方法、它们统称为泛型。
ArrayList类就是一个泛型类
ArrayList集合的设计者在定义ArrayList集合时,就已经明确ArrayList集合时给别人装数据用的,但是别人用ArrayList集合时候,装什么类型的数据他不知道,所以就用一个<E>表示元素的数据类型。当别人使用
ArrayList集合创建对象时,new ArrayList<String>就表示元素为String类型,new ArrayList<Integer>表示元素为Integer类型。
-
-
泛型的好处:在编译阶段可以避免出现一些非法的数据。
-
泛型的本质:把具体的数据类型传递给类型变量。
-
自定义泛型类
//这里的<T,W>其实指的就是类型变量,可以是一个,也可以是多个。 public class 类名<T,W>{ }
- 示例:
//定义一个泛型类,用来表示一个容器 //容器中存储的数据,它的类型用<E>先代替用着,等调用者来确认<E>的具体类型。 public class MyArrayList<E>{ private Object[] array = new Object[10]; //定一个索引,方便对数组进行操作 private int index; //添加元素 public void add(E e){ array[index]=e; index++; } //获取元素 public E get(int index){ return (E)array[index]; } }
自定义泛型接口
- 在实际工作中,一般也都是框架底层源代码把泛型接口写好,我们实现泛型接口就可以了.
泛型接口其实指的是在接口中把不确定的数据类型用
<类型变量>表示。定义格式如下://这里的类型变量,一般是一个字母,比如<E> public interface 接口名<类型变量>{ }示例:做一个系统要处理学生和老师的数据,需要提供2个功能,保存对象数据、根据名称查询数据,要求:这两个功能处理的数据既能是
老师对象,也能是学生对象。public class Teacher{ }public class Student{ }定义一个
Data<T>泛型接口,T表示接口中要处理数据的类型。public interface Data<T>{ public void add(T t); public ArrayList<T> getByName(String name); }
这个接口可以让不同的类实现操作不同的数据
//此时确定Data<E>中的E为Teacher类型, //接口中add和getByName方法上的T也都会变成Teacher类型 public class TeacherData implements Data<Teacher>{ public void add(Teacher t){ } public ArrayList<Teacher> getByName(String name){ } }//此时确定Data<E>中的E为Student类型, //接口中add和getByName方法上的T也都会变成Student类型 public class StudentData implements Data<Student>{ public void add(Student t){ } public ArrayList<Student> getByName(String name){ } }
泛型方法
public <泛型变量,泛型变量> 返回值类型 方法名(形参列表){ }
- 在返回值类型和修饰符之间有定义的才是泛型方法
public class Test{
public static void main(String[] args){
//调用test方法,传递字符串数据,那么test方法的泛型就是String类型
String rs = test("test");
//调用test方法,传递Dog对象,那么test方法的泛型就是Dog类型
Dog d = test(new Dog());
}
//这是一个泛型方法<T>表示一个不确定的数据类型,由调用者确定
public static <T> test(T t){
return t;
}
}
泛型限定
泛型限定的意思是对泛型的数据类型进行范围的限制。有如下的三种格式
- <?> 表示任意类型
- <? extends 数据类型> 表示指定类型或者指定类型的子类
- <? super 数据类型> 表示指定类型或者指定类型的父类
假设有Car作为父类,BENZ,BWM两个类作为Car的子类
class Car{} class BENZ extends Car{} class BWN extends Car{} public class Test{ public static void main(String[] args){ //1.集合中的元素不管是什么类型,test1方法都能接收 ArrayList<BWM> list1 = new ArrayList<>(); ArrayList<Benz> list2 = new ArrayList<>(); ArrayList<String> list3 = new ArrayList<>(); test1(list1); test1(list2); test1(list3); //2.集合中的元素只能是Car或者Car的子类类型,才能被test2方法接收 ArrayList<Car> list4 = new ArrayList<>(); ArrayList<BWM> list5 = new ArrayList<>(); test2(list4); test2(list5); //2.集合中的元素只能是Car或者Car的父类类型,才能被test3方法接收 ArrayList<Car> list6 = new ArrayList<>(); ArrayList<Object> list7 = new ArrayList<>(); test3(list6); test3(list7); } public static void test1(ArrayList<?> list){ } public static void test2(ArrayList<? extends Car> list){ } public static void test3(ArrayList<? super Car> list){ } }
泛型擦除
就是说泛型只能编译阶段有效,一旦编译成字节码,字节码中是不包含泛型的。而且泛型只支持引用数据类型,不支持
基本数据类型。
反编译后:ArrayList后面没有泛型


















1649

被折叠的 条评论
为什么被折叠?



