1.JDK和JRE的区别
JRE是Java Runtime Environment的缩写,顾名思义是java运行时环境,包含了java虚拟机,java基础类库。
Jdk是Java Development Kit的缩写,顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
2. == 和 equals 的区别是什么?
==的作用是判断两个引用的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型 == 比较的值,引用数据类型 == 比较的是内存地址)。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
两个对象equals相等,则它们的hashcode必须相等,反之则不一定。
两个对象==相等,则其hashcode一定相等,反之不一定成立。
重写equals方法,就必须要重写hashcode方法
4.当两个对象hashcode相同时会发生什么
因为hashcode相同,所以他们bucket的位置相同,碰撞会发生。加入到同一个链表中,在JDK1.8中,链表长度为8时,会变成红黑树。
5.final 在 java 中有什么作用?
final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。
(1)修饰类:表示该类不能被继承;
(2)修饰方法:表示方法不能被重写;
(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
6.Math.round()
Math.round()的原理是+0.5然后向下取整
Math.floor() 地板 比它小的第一个整数
Math.ceil() 天花板 比它大的第一个整数
7.基本数据类型
8中基本数据类型
byte,short,char,int,long,float,double,boolean
String是对象!!!java就基本数据类型和引用类型
8.java 中操作字符串都有哪些类?它们之间有什么区别?
String,StringBuffer,StringBuilder
String是不可变的,每次操作都会产生新的对象。
StringBuilder和StringBuffer是字符缓冲区,可变的字符序列。
StringBuffer线程安全,同步锁(synchronized),多线程仍可以保证数据安全
StringBuilder线程不安全,多线程无法保证数据安全
9.String str1= "" 和 str2 = new String()的区别
去常量池中查看是否有相同Unicode编码的字符串常量。如果有将str1指向该常量,如果没有则创建一个内容为”str1”的字符串常量,将str1的引用指向该常量。
new String()会在堆中创建一个新的对象
第一次调用 new String("xyz"); 时,会在堆内存中创建一个字符串对象,同时在字符串常量池中创建一个对象 "xyz"
第二次调用 new String("xyz"); 时,只会在堆内存中创建一个字符串对象,指向之前在字符串常量池中创建的 "xyz"
10.str="a"+"b"+"c"创建了几个对象
只创建了一个对象
编译的时候会对字符串常量相加的表达式进行优化,在编译时去掉加号,直接将其编译成一个字符串相连的结果。
要注意变量相加是不可以的,因为编译器不能判断运行期间字符串变量是否会变化。
11.如何将字符串反转
直接使用StringBuilder或StringBuffer,自己实现也很简单
12.String的常用方法
equals、toCharArray,substring,charAt,trim,split,equalsIgnoreCase,compareTo,compareToIgnoreCase,indexOf,lastIndexOf,valueOf,codePointAt:指定下标的字符的Unicode编码,
13.普通类和抽象类有哪些区别?
抽象类不能被实例化
抽象类可以有抽象方法,抽象方法只需申明,无需实现
含有抽象方法的类必须申明为抽象类
抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
抽象方法不能被声明为静态
抽象方法不能用 private 修饰
抽象方法不能用 final 修饰
14.接口与抽象类的区别
共同点是都不可以实例化,都可以含有抽象方法
1.只能继承一个类,却可以实现多个接口
2.接口是interface,实现接口用implements,抽象类用abstract修饰
3..抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的,接口的字段都隐含着public static final;
15.IO流可以分为那几种
按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流(任何内容都可以转为字节)和字符流(只能操作纯文本数据);
按照功能划分为节点流:包裹源头,实现基本功能。和处理流:。
InputStream InputStreamis=newFileInputStream(newFile("D:/test.txt"));/
OutputStream字节流
Reader Writer 字符
处理流:InputStreamis=newBufferedInputStream(newFileInputStream("D:/hehe.txt"));
处理流采用了装饰器模式。都实现了同一个接口,然后对方法进行增强。
数据流,允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
DataInputStream in=newDataInputStream(newBufferedInputStream(newFileInputStream(path)));
对象流
ObjectInputStream() readObject() writeObject()
16.Files的常用方法
Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。
17.BIO,NIO,AIO的区别
IO:传统的网络通讯模型,就是BIO,同步阻塞IO
它其实就是服务端创建一个ServerSocket, 然后就是客户端用一个Socket去连接服务端的那个ServerSocket, ServerSocket接收到了一个的连接请求就创建一个Socket和一个线程去跟那个Socket进行通讯。
接着客户端和服务端就进行阻塞式的通信,客户端发送一个请求,服务端Socket进行处理后返回响应。
在响应返回前,客户端那边就阻塞等待,上门事情也做不了。
这种方式的缺点:每次一个客户端接入,都需要在服务端创建一个线程来服务这个客户端
这样大量客户端来的时候,就会造成服务端的线程数量可能达到了几千甚至几万,这样就可能会造成服务端过载过高,最后崩溃死掉。
NIO:是一种同步非阻塞IO, 就是多路复用IO。jdk7之后底层是epoll模型。
Java Nio关键是channels selectors buffer
其实相当于就是一个线程处理大量的客户端的请求,通过一个线程轮询大量的channel,每次就获取一批有事件的channel,然后对每个请求启动一个线程处理即可。这里的核心就是非阻塞,就那个selector一个线程就可以不停轮询channel,所有客户端请求都不会阻塞,直接就会进来,大不了就是等待一下排着队而已。
AIO:异步非阻塞IO,基于Proactor模型实现。
每个连接发送过来的请求,都会绑定一个Buffer,然后通知操作系统去完成异步的读,这个时间你就可以去做其他的事情
等到操作系统完成读之后,就会调用你的接口,给你操作系统异步读完的数据。这个时候你就可以拿到数据进行处理,将数据往回写
在往回写的过程,同样是给操作系统一个Buffer,让操作系统去完成写,写完了来通知你。
这俩个过程都有buffer存在,数据都是通过buffer来完成读写。
这里面的主要的区别在于将数据写入的缓冲区后,就不去管它,剩下的去交给操作系统去完成。
操作系统写回数据也是一样,写到Buffer里面,写完后通知客户端来进行读取数据。
18.基本数据类类型使用equals
JVM缓存部分基本数据常用的包装类对象, Integer -128 ~ 127 是被缓存的
不同类型的比较equals会先比较类型
包装类对象和基本数据类型使用==和equals,基本数据类型会自动装箱
19.什么是自动装箱和自动拆箱
装箱:基本类型转变为包装器类型的过程。
拆箱:包装器类型转变为基本类型的过程。
装箱是通过调用包装器类的 valueOf 方法实现的
拆箱是通过调用包装器类的 xxxValue 方法实现的,xxx代表对应的基本数据类型。
20.finally语句一定会执行吗?
答案是不一定。存在很多特殊情况导致 finally 语句块不执行。如:
直接返回未执行到 try-finally 语句块
抛出异常未执行到 try-finally 语句块
系统退出未执行到 finally 语句块
21.static与final的区别
都不能用于修饰构造方法。static 可以修饰类的代码块,final 不可以。
static 不可以修饰方法内的局部变量,final 可以。
static 修饰的属性和方法属于类,可以用类名.静态属性 / 方法名 访问static 修饰的代码块表示静态代码块,当 Java 虚拟机(JVM)加载类时,就会执行该代码块,只会被执行一次static 修饰的属性,也就是类变量,是在类加载时被创建并进行初始化,只会被创建一次static 修饰的变量可以重新赋值static 方法中不能用 this 和 super 关键字sftatic 方法必须被实现,而不能是抽象的abstractstatic 方法不能被重写
final 修饰表示常量、一旦创建不可改变
final 标记的成员变量必须在声明的同时赋值,或在该类的构造方法中赋值,不可以重新赋值
final 方法不能被子类重写
final 类不能被继承,没有子类,final 类中的方法默认是 final 的
22.String的repalce和replaceAll的区别
replace方法:支持字符和字符串的替换。replaceAll方法:基于正则表达式的字符串替换。
23.Java中的 << >> >>> 是什么
<< 表示左移,不分正负数,低位补0
>> 表示右移,如果该数为正,则高位补0,若为负数,则高位补1
>>> 表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0
24.javap的作用是什么
javap 是 Java class文件分解器,可以反编译,也可以查看 java 编译器生成的字节码等。
25.throw和throws的区别
throw:表示方法内抛出某种异常对象(只能是一个),必须被捕获
用于程序员自行产生并抛出异常执行到 throw 语句则后面的语句块不再执行
throws"方法的定义上使用 throws 表示这个方法可能抛出某些异常(可以有多个)
26.Java内部类
成员内部类,会编译出两个class文件
方法内部类。只能在定义该内部类的方法内实例化。方法内部类对象不能使用该内部类所在方法的非final局部变量。当一个方法结束,其栈结构被删除,局部变量成为历史。但该方法结束后,在方法内创建的内部类对象可能仍然存在于堆中
静态内部类:静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它
静态嵌套类仅能访问外部类的静态成员和方法
内部类提供了进入实现的接口的窗口
27.反射
Java 反射,就是在运行状态中
获取任意类的名称、package 信息、所有属性、方法、注解、类型、类加载器、modifiers(public、static)、父类、现实接口等
获取任意对象的属性,并且能改变对象的属性
调用任意对象的方法
判断任意一个对象所属的类
实例化任意一个类的对象
Java 的动态就体现在反射。通过反射我们可以实现动态装配,降低代码的耦合度;动态代理等。反射的过度使用会严重消耗系统资源。
JDK 中 java.lang.Class 类,就是为了实现反射提供的核心类之一。
一个 jvm 中一种 Class 只会被加载一次。
28.动态代理
动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。
Java 中实现动态的方式:
JDK 中的动态代理
Java类库 CGLib
Spring的 AOP 功能模块就是采用动态代理的机制来实现切面编程
29.什么是java序列化?什么情况下需要序列化?
序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
某个类可以被序列化,则其子类也可以被序列化
对象中的某个属性是对象类型,需要序列化也必须实现 Serializable 接口
声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
反序列化读取序列化对象的顺序要保持一致
30. 什么场景要对象克隆
方法需要 return 引用类型,但又不希望自己持有引用类型的对象被修改。
程序之间方法的调用时参数的传递。有些场景为了保证引用类型的参数不被其他方法修改,可以使用克隆后的值作为参数传递。
31.深拷贝和浅拷贝区别是什么
浅拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。
深拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针和堆内存中的对象。
32.如何实现对象克隆与深拷贝
1、必须实现实现 Cloneable 接口,重写 clone() 方法。
2、不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。
3.Object 的 clone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。
可以使用下面的两种方法,完成 对象的深拷贝。
方法1、对象的属性也实现 Cloneable 接口,在克隆对象时也手动克隆属性。
方法2、结合序列化JDK java.io.Serializable 接口完成深拷贝。常常可以先使对象实现Serializable接口,然后把对象写到一个流里,再从流里读出来,便可以重建对象。
33.Java跨平台运行的原理
1、.java 源文件要先编译成与操作系统无关的 .class 字节码文件,然后字节码文件再通过 Java 虚拟机解释成机器码运行。
2、.class 字节码文件面向虚拟机,不面向任何具体操作系统。
3、不同平台的虚拟机是不同的,但它们给 JDK 提供了相同的接口。
4、Java 的跨平台依赖于不同系统的 Java 虚拟机。
34.Java的安全性体现在哪里?
1、使用引用取代了指针,指针的功能强大,但是也容易造成错误,如数组越界问题。
2、拥有一套异常处理机制,使用关键字 throw、throws、try、catch、finally
3、强制类型转换需要符合一定规则
4、字节码传输使用了加密机制
5、运行环境提供保障机制:字节码校验器->类装载器->运行时内存布局->文件访问限制
6、不用程序员显示控制内存释放,JVM 有垃圾回收机制
35.&和&&的作用和区别
&逻辑与,& 两边的表达式都会进行运算,整数的位运算符
&& 短路与,&& 左边的表达式结果为 false 时,&& 右边的表达式不参与计算
|逻辑或,| 两边的表达式都会进行运算,整数的或运算符
|| 短路或,|| 左边的表达式结果为 true 时,|| 右边的表达式不参与计
36.如何让计算机最高效的算出2乘以8
2 <<3位运算符 <<,是将一个数左移 n 位,相当于乘以了 2 的 n 次方。一个数乘以 8 只要将其左移 3 位即可。CPU 直接支持位运算,效率最高
37.可变参数的作用和特点是什么?
在不确定参数的个数时,可以使用可变参数。
每个方法最多只有一个可变参数。可变参数必须是方法的最后一个参数。可变参数可以设置为任意类型:引用类型,基本类型参数的个数可以是 0 个、1 个或多个。可变参数也可以传入数组。无法仅通过改变 可变参数的类型,来重载方法。通过对 class 文件反编译可以发现,可变参数被编译器处理成了数组。
38.方法重载和重写是什么?有什么区别?
声明为 final 的方法不能被重写。声明为 static 的方法不能被重写。声明为 private 的方法不能被重写。
方法签名,方法名和参数类型
39.子类构造器的方法的执行过程是什么样的
如果子类的构造方法中没有通过 super 显式调用父类的有参构造方法,也没有通过 this 显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。这种情况下,写不写 super(); 语句,效果是一样的
如果子类的构造方法中通过 super 显式调用父类的有参构造方法,将执行父类相应的构造方法,不执行父类无参构造方法
如果子类的构造方法中通过 this 显式调用自身的其他构造方法,将执行类中相应的构造方法
如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类 Object 类的无参构造方法为止
40.什么是Java的多态?
同一个接口,使用不同的实例而执行不同操作。同一个行为具有多个不同表现形式或形态的能力。
实现多态的三个条件
继承的存在。继承是多态的基础,没有继承就没有多态
子类重写父类的方法,JVM 会调用子类重写后的方法
父类引用变量指向子类对象
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
Java 中使用父类的引用变量调用子类重写的方法,即可实现多态。
好处:
消除类型之间的耦合关系
可替换性(substitutability)
41.java.sql.Date和java.util.Date的区别
java.sql.Date 是 java.util.Date 的子类
java.util.Date 是 JDK 中的日期类,精确到时、分、秒、毫秒
java.sql.Date 与数据库 Date 相对应的一个类型,只有日期部分,时分秒都会设置为 0,如:2019-10-23 00:00:00
要从数据库时间字段取 时、分、秒、毫秒数据,可以使用 java.sql.Timestamp
42.
加载 class 时首先完成 static 方法装载,非 static 属性和方法还没有完成初始化,所以不能调用。
43.说说你对面向对象的理解
对 Java 语言来说,一切皆是对象。
对象有以下特点:
对象具有属性和行为
对象具有变化的状态
对象具有唯一性
对象都是某个类别的实例
一切皆为对象,真实世界中的所有事物都可以视为对象
三大特征:封装,继承,多态
五大原则:
单一职责原则:是指一个类的功能要单一
开放封闭原则:对扩展开放,对修改封闭
里氏替换原则:子类型必须能够替换掉它们的父类型。
依赖倒转原则:要针对接口编程,而不要针对实现编程。
接口隔离原则:使用多个专门的接口比使用单一的总接口要好。
迪米特法则:(最少知道原则)一个对象应该对其他对象有最少的了解。降低类之间的耦合
44.内存泄漏和内存溢出的区别
内存溢出(out of memory):指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory。
内存泄露(memory leak):指程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会导致内存被占光。
memory leak 最终会导致 out of memory。
45.不通过构造方法能创建对象吗?
Java 创建对象的方式:
1.用 new 语句创建对象
2.运用反射,调用 java.lang.Class 或 java.lang.reflect.Constructor 类的 newInstance() 方法
3.调用对象的 clone() 方法
4.运用反序列化手段,调用 java.io.ObjectInputStream 对象的 readObject() 方法
46.匿名内部类可以继承类或实现接口吗?为什么?
匿名内部类本质上是对父类方法的重写 或 接口的方法的实现
从语法角度看,匿名内部类创建处是无法使用关键字继承类 或 实现接口
原因:
1.匿名内部类没有名字,所以它没有构造函数。因为没有构造函数,所以它必须通过父类的构造函数来实例化。即匿名内部类完全把创建对象的任务交给了父类去完成。
2.匿名内部类里创建新的方法没有太大意义,新方法无法被调用。
3.匿名内部类一般是用来覆盖父类的方法。
4.匿名内部类没有名字,所以无法进行向下的强制类型转换,只能持有匿名内部类对象引用的变量类型的直接或间接父类。
47.Math.Random()方法的使用
Math.Random()会随机生成[0,1)之间的随机数,一般情况下大家让Math.Random()乘以一个数,例如a。这样我们就可以生成[0,a)之间的随机数([0*a=0,1*a=a)),如果再在前面添加一个int,那么我们就可以产生[0,a)之间的正整数。此处需要注意的是[0,a)是前开后闭的,即前包含后不包含。
扩展:产生n-m的随机数
Math.random()*(m-n)+n
48.java.util.Random类
Random random=new Random();//以系统当前时间作为随机数生成的种子
random.nextInt(10);产生0-10的一个随机数
49.静态内部类和非静态内部类有什么区别
静态内部类可以有静态方法、属性;非静态内部类则不能有静态方法、属性
静态内部类只能访问外部类的静态成员,不能访问外部类的非静态成员;非静态内部类能够访问外部类的静态和非静态成员
静态内部类不依赖于外部类的实例,直接实例化内部类对象;非静态内部类通过外部类的对象实例生成内部类对象
50.java_home 无法再编译时指定,最多在命令行,让操作系统直接找到可执行程序
编译程序时,环境变量包括 java_home 和 class path
javac.exe -d 参数可指定生成类文件的位置
51.存在i+1< i的数吗?为什么?
存在,Integer.MAX_VALUE 会溢出的
52.Class类的getDeclaredFields()与getFields()方法的区别?
getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
53.构造方法可以被重载,但是不可以重写
54.System.out.println('a'+1);的结果是
98,int 与 char 相加,char 会被强转为 int 型,'a'asc码是97
55.静态与非静态成员变量区别
生命周期不同:非静态成员变量随着对象的创建而存在;静态成员变量随着类的加载而存在
调用方式不同:非静态成员变量用 对象名.变量名 调用;静态成员变量用 类名.变量名,JDK1.7以后也能用对象名.变量名调用
别名不同:非静态成员变量也称为实例变量;静态变量称为类变量
数据存储位置不同:成员变量数据存储在堆内存的对象中,对象的特有数据;静态变量数据存储在方法区(共享数据区)的静态区,对象的共享数据
56.short s1 = 1; s1 = s1 + 1;short s1 = 1; s1 += 1;的区别
前面的会报错,因为s1+1结果是int型,需要强制转换,而+=这个运算具有隐式转换的功能,所以不会报错。
57.为什么不能根据返回类型来区分方法重载?
如果可以根据返回值类型来区分方法重载,那在仅仅调用方法不获取返回值的使用场景,JVM 就不知道调用的是哪个返回值的方法了。
58.Class类的作用是什么?如何获取Class对象
Class 类是 Java 反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。
对象名.getClass() 对象名.getSuperClass()。Class.forName("oracle.jdbc.driver.OracleDriver");
类名.class
59.引用数据类型的默认值为 null new String[10]
60.写一个方法实现String类的replaceAll方法
return Pattern.compile(regex).matcher(s).replaceAll(replacement);
61.为什么String类被设计用final修饰?
1.String 类是最常用的类之一,为了效率,禁止被继承和重写
2.为了安全。String 类中有很多调用底层的本地方法,调用了操作系统的 API,如果方法可以重写,可能被植入恶意代码,破坏程序。Java 的安全性也体现在这里。
62. 什么是assert?
assert:断言
一种常用的调试方式,很多开发语言中都支持这种机制
通常在开发和测试时开启,可以用来保证程序最基本、关键的正确性,为了提高性能,发布版的程序通常关闭断言
assert 表达式1;
assert 表达式1 : 错误表达式 ;
表达式1 是一个布尔值,错误表达式可以得出一个值,用于生成显示调试信息的字符串消息
63. 类的实例化方法调用顺序
类加载器实例化时进行的操作步骤:加载 -> 连接 -> 初始化
代码书写顺序加载父类静态变量和父类静态代码块
代码书写顺序加载子类静态变量和子类静态代码块
父类非静态变量(父类实例成员变量)
父类构造函数
子类非静态变量(子类实例成员变量)
子类构造函数
64. JDK8中Stream接口的常用方法
Stream 接口中的方法分为中间操作和终端操作,具体如下。
中间操作
filter:过滤元素 map:映射,将元素转换成其他形式或提取信息 limit:截断流,使其元素不超过给定数量 sorted:排 distinct:去重
终端操作
findFirst:查找第一个元素
collect:把流转换成其他形式,如集合 List、Map、Integer
forEach:消费流中的每个元素并对其应用 Lambda,返回 void
reduce:归约,如:求和、最大值、最小值
count:返回流中元素的个数
65. 说说反射在你实际开发中的使用
反射使用不好,对性能影响比较大,一般项目中很少直接使用。
反射主要用于底层的框架中,Spring 中就大量使用了反射,比如:
用 IoC 来注入和组装 bean,动态代理、面向切面、bean 对象中的方法替换与增强,也使用了反射
定义的注解,也是通过反射查找
66. 什么是泛型?为什么要使用泛型?
"参数化类型",将类型由具体的类型参数化,把类型也定义成参数形式(称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。提供了编译时类型安全监测机制,该机制允许程序员在编译时监测非法的类型。
为什么要用泛型?
1)使用泛型编写的程序代码,要比使用 Object 变量再进行强制类型转换的代码,具有更好的安全性和可读性。
2)多种数据类型执行相同的代码使用泛型可以复用代码。
比如集合类使用泛型,取出和操作元素时无需进行类型转换,避免出现 java.lang.ClassCastException 异常