1.请说说Java的特点和优点,为什么要选择Java:
- Java是一门非常纯粹的面向对象的编程语言
- Java的特点:Java省去了C++的多继承,指针等
- Java的优点:1.面向对象,2.平台无关性,3.简单性,4.解释执行,5.多线程:6.分布式,7.健壮性,8.高性能:9.安全性
2.对面向对象的理解:
- 面向对象三大基本特性:1.封装,2.继承,3.多态
- 封装:将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,让外部程序通过该类提供的方法来实现内部信息的操作和访问,提高了代码的可维护性。
- 继承:通过extends实现类的继承,实现代码复用的重要手段。
- 多态:父类变量指向子类实例
3.Java8的新特性:
- Lambda表达式:可以更简洁地表示单方法接口的实例
- 方法引用:可以直接引用已有Java类或对象(实例)的方法或构造器,方法引用使用一对冒号 :: ,可以代码更加紧凑简洁
- 默认方法:允许在接口中定义默认方法,默认方法必须使用default修饰
- Stream API:把真正的函数式编程风格引入到Java,可以对集合进行批量操作
- Data Time API:加强对日期与时间的处理
4.包装类的自动拆箱和自动装箱:
- 包装类的的作用:是解决Java的八种基本数据类型不面向对象的缺陷,有了包装类,所有的类都可以继承object类。
- 自动装箱,自动拆箱是JDK1.5提供得到功能
- 自动装箱:基本类型的数据和变量可以直接赋值给包装类的变量
- 自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量
5.String是不可变类型还是可变类型,为什么不可变:
-
答1:不可变类型
-
答2:String被final修饰,被创建的对象只能被赋值一次
public final class String implements java.io.Serializable, Comparable<String>, CharSequence,Constable, ConstantDesc { ....... }
6.String和new String()的区别:
-
String str1="abc" 首先查看常量池是否有“abc”这个对象,存在着返回地址,不存在则创建“abc”对象并返回地址。
-
String str1=new String("abc") 在堆中创建一个对象”abc“,str1接收的是对象的地址
String a1="aa"; String a2=new String("aa"); System.out.println(a1==a2); //false System.out.println(a1.equals(a2)); //true//intern方法将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 都会把串池中的对象返回 String s4="a"; String s6=s4.intern(); System.out.println(s4==s6)); //true String a1="aa"; String a2=new String("aa").intern(); System.out.println(a1==a2); //true System.out.println(a2==a2.intern()); //true
7.String,StringBuffer,StringBuilder区别:
- String是只读字符串
- StringBuffer/StringBuilder是Java提供的两个类来封装字符串,是没有被final修饰,并提供了一些修改字符串修改的方法
- StringBuffer和StringBuilder的方法完全相同
- 区别在于StringBuilder是单线程下使用,它的所有方法都被有被synchronized修饰
8.final, finally, finalize 的区别:
- final意为最终,用来修饰java的类(不可继承),方法(不能被重写),变量(只能被赋值一次)
- finally属于异常处理try-catch,finally一定会在return之前执行,但如果finally中出现return或者throw,会使try-catch中的return或者catch实效。
- finalize()是Java中Object的一个方法,是对象被垃圾回收之前的最后⼀根救命稻草
9.反射的过程和作用:
-
过程:反射就是java文件编译成字节码文件后,通过字节码文件找到一类中的成员
-
作用:反射机制是指程序在运行时能获取对象的属性和方法的功能
-
获取Class对象的三种方式:
- .getClass(),2. .class,3. Class
-
优缺点:
- 运行期间能够动态的获取类,提高代码得到灵活性
- 性能比直接的Java代码要慢
-
应用场景:
- 1.spring的xml配置模式,2.动态代理模式
10.请写出jdbc连接过程:
//导入jar包
Class.forName("com.mysql.jdbc.Driver");
//注册驱动
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm?userSSL=false","root","");
//获取数据库连接对象
Statement stmt=con.getcreatStatement();
//执行sql语句,DML语句
int count=stmt.executeUpdate(sql);
//执行sql语句,DQL语句
Result re=stmt.executeQuery(sql);
while(rs.next()){
int a=rs.getInt("id");
.....
}
12.基本数据类型和引用数据类型的区别:
- 基本数据类型:是对值得直接操作,它主要分配在栈中
- 引用数据类型:它得引用在栈中,对象得创建在内存堆中,引用指向堆中的地址;引用数据类型默认继承于Object基类
13.equals和==的区别:
-
==:
- 如果是基本数据类型,比较两个值是否相等
- 如果是引用数据类型,比较两个地址是否相等
-
equals:
- 如果没有重写该方法,比较的是地址是否相等
- 如果重写了该方法,比较的是内容是否相等(String,Integer会对equals自动重写)
14.HashCode() 和equals() 的区别
- HashCode() 方法的主要用途是获取哈希码,equals() 方法主要用来比较两个对象内容是否相等
- HashCode() 和equals() 的约定:如果两个对象相等,他们必须有相同的哈希码;equals() 比较两个对象相等时HashCode() 一定相等
15.为什么重写equals() 就要重写hashCode():
- 重写的原因:由于HashCode() 和equals() 具有联动关系,equals重写时,HashCode() 进行重写使得这两个方法始终满足相关的约定
15.接口和抽象类的区别:
- 抽象类:1.可以定义构造器,2.可以有抽象方法也可以有具体方法,3.抽象类的权限修饰符可以是多种,4.抽象类可以定义成员变量,5.抽象类可以包含静态方法,6.一类只能继承一个抽象类
- 接口:1.不能定义构造器,2.方法全部是抽象方法,3.接口的成员全部都是public的,4.定义的成员变量实际是常量,5.不能有静态方法,6.一个类可以实现多个接口
16.重载和重写的区别:
-
重载:
- 重载发生在同一类中
- 要求方法名必须相同,参数列表不同
- 并且和方法返回值和访问修改符无关
-
重写:
- 重写发生在父子类中
- 子类重写方法的名称和参数列表必须和父类相同
- 子类的返回值类型必须是父类的返回值及子类型,抛出异常类型必须是父类的异常类型及子类型
- 子类重写方法的访问修饰符必须大于父类,若父类方法的修饰符为private则子类不能重写该方法
17.线程的状态:
-
初识状态(new):new一个线程就是创建状态
-
运行状态(runnable):
- 就绪:执行start方法就是就绪状态
- 运行:处于就绪状态的线程获取了cpu之后就是运行状态
-
阻塞状态(blocked):线程正在等待获取监视器锁
-
等待状态(waiting):线程正在等待其他线程的通知或中断
-
超时等待状态(timed_waiting):在等待的基础上增加了超时时间,超出时间自动返回
-
终止状态(terminated):线程终止运行,最后的run() 方法执行完或者抛出一个异常时就销毁了。
11.wait()和sleep()的区别:
-
所属的类型不同:
- wait() 是Object类的实例方法,调用该方法的线程将进入waiting(等待)状态
- sleep() 是Thread类的静态方法,调用该方法的线程将进入TIMED_WAITING(超时等待)状态
-
对锁的依赖不同:
- wait() 依赖于synchronized锁,通过监视器(Monitor)进行调用后线程会释放锁
- sleep() 不依赖于任何锁,所以在调用后它也不会释放锁
-
返回的条件不同:
- 调用wait() 进入等待状态的线程,需要由notify(),notifyAll() 唤醒,从而返回
- 调用sleep() 进入超时等待的线程,需要在超时时间到达后自动返回
18.线程的生命周期:
- 1.新建,2.就绪,3.运行,4.堵塞,5.死亡
19.创建线程的方法:
- 1.继承Thread类,2.实现Runnable接口,3.实现callable接口,4.使用线程池或者Executor框架
20.实现Callable接口和实现Runnable接口的区别:
-
相同点:
- 都是接口
- 都可以采用多线程程序
- 都是交给Thread采用start方法启动
-
不同点:
- 返回值:Callable的call方法有返回值,需要和FutureTask的泛型相同;Runnable没有返回值
- 异常:Callable的call方法可以向上抛出异常,也可以直接在方法内捕获异常;Runnable的call方法只能在方法内捕获异常,不能向上抛出
20.什么是并发,什么是并行:
- 并发:多个线程在同一时间段发生
- 并行:多个线程在同一时刻发生
20.进程与线程的区别:
- 线程是CPU调度的最小单元,一个进程可以拥有多个线程
- 各个线程都拥有各自的程序计数器,虚拟机栈,本地内存栈
- 而一个线程中多个线程可以共享其方法区和堆
- 某个线程崩溃时,它的进程也会崩溃;但是一个进程崩溃,不会影响到其他进程
20.请你说说多线程:
-
线程是CPU调度的最小单元,操作系统采用的是批处理的方式,所以支持使用多线程
-
一个进程可以拥有多个线程
-
各个线程都拥有各自的程序计数器,虚拟机栈,本地内存栈,并且能够共享进程内的资源(方法区和堆)
-
多线程的优点:
- 1.减少程序响应时间,2.提高CPU利用效率,3.创建和切换开销小,4.数据共享效率高,5.简化程序结构
21.线程安全:
-
线程安全问题:多个线程同时操作统一共享资源的时候可能会出现业务安全问题
-
线程安全:有多个线程同时运行,而多个线程同时运行这段代码,多线程下运行的结果和单线程一致就为线程安全
-
线程同步:
- 主要通过加锁的方式实现线程同步
- 方式一:synchronized同步代码块,方式二:synchronized同步方法,方式三:Lock锁、
22.线程通信的方式:
- 线程通信的定义:线程间互相发送信息
- 常见的线程通信方式:1.利用Monitor实现线程通信,2.利用Condition实现线程通信
- 使用不同的线程同步方式也就对应的使用不同的线程通信方式
- 使用synchronize同步时就会使用monitor来实现线程通信,使用Lock进行同步时就是使用Condition来实现线程通信
23.线程池的理解:
-
线程池就是一个可以复用线程的技术
-
不使用线程池:如果用户每发起一个请求,后台就创建一个线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的。这样严重影响系统的性能
-
核心参数:
- 1.corePoolSize( 核心线程数 ),2.workQueue( 等待队列 ),3.maxinumPoolSize( 最大线程数 ),4.handler( 拒绝策略 ),5.keepAliveTime( 空闲线程存活时间 )
-
线程池的创建:
- 方式一:使用ExcutorService的实现类ThreadPoolExecutor自创建一个线程池对象
- 方式二:使用Executors(工具类)调用方法返回不同特点的线程池对象
-
线程复用:在线程池中,通过同一个线程去执行不同的任务
-
线程复用的原理:线程池将线程与任务进行解耦,摆脱了Thread创建一个线程必须对应一个任务的限制;如有3个线程100个任务,从头到尾只有3个线程被创建
24.
24.对ThreadLocal的理解:
-
ThreadLocal即线程变量,ThreadLocal是采用哈希表的方式来为每个线程都提供一个变量的副本
-
作用:通过把数据放在ThreadLocal中让每个线程创建一个该变量副本,避免并发访问的线程安全问题
-
实现原理:每个Thread对象中都有一个ThreadLocal类的内部类ThreadLocalMap对象,它是一个键值形式的容器,以ThreadLocal对象的get和set方法来存取共享变量值
public class BaseContext { private static ThreadLocal<Long> threadLocal = new ThreadLocal<>(); //设置值 public static void setCurrentId(Long id){ threadLocal.set(id); } //获取值 public static Long getCurrentId(){ return threadLocal.get(); } }
24.ThreadLocal的内存泄露:
-
内存泄漏:不会被使用的对象或者变量无法被回收
-
内存泄露的原因:由于Entry中value是强引用,key为弱引用;当CG回收ThreadLocal后,由于value是强引用,无法被回收,所以造成了内存泄漏
-
ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应的key就会导致内存泄漏,而并不是因为弱引用
-
解决方案:
- 每次使用完ThreadLocal对象后都调用它的remove()的方法清除数据
- 将ThreadLocal引用定义为private static,这样就一直存在ThreadLocal的强引用,也就保证能通过ThreadLocal弱引用访问到Entry中的value值,进而清除数据。
25.Hash冲突
- Hash冲突:向Hash表中插入两个不同的值时,计算出的HashCode值相同
- 解决方法:1.开放地址法,2.线性探测法,3.二次探测法,4.再哈希法,5.链地址法,6.建立公共溢出区
- ThreadLocal使用开放定址法(寻找一个空地址存储)解决hash冲突,HashMap使用链地址法(对于相同hash值的元素使用链表进行连接)解决hash冲突
28.对Java集合的了解:
-
Java中的集合类主要都有Collection和Map这个接口派生而生
-
所有的集合类都是List,Set,Queue,Map这四个接口实现
-
Collection接口:
-
List接口:有序的,可重复的数据集合
- ArrayList实现类:
- LinkedList实现类:
-
Set接口:无序的,不可重复的数据集合
- HashSet实现类:底层采用哈希表存储的数据,
- TreeSet实现类:基于红黑树的数据结构实现排序
-
Queue接口:先进先出的队列
-
-
Map接口:是具有映射关系的集合,key也不能重复
- HashMap实现类:
- HashTable实现类:
- .......
29.List和Set的区别:
-
相同点:他们都继承自Collection接口
-
不同点:
- list是一个有序,可重复的容器
- set是一个无序,不可重复的容器,只支持增强for循环和迭代器
29.线程安全的集合:
- 古老的集合(已经不推荐使用):1.List接口下的vector实现类,2.Map接口下的Hashtable实现类(都是基于Synchronized实现)
- 对于非安全实现类:使用Collections工具类的sychronized方法转换为线程安全的集合
- jdk1.5之后使用concurrent包下支持大量并发访问的集合类,例:ConcurrentHashMap / CopyOnWriteArrayList
30.ArrayList,LinkedList,Vector三者的区别:
- 三者都实现了List接口
- 底层:ArrayList,Vector都是数组;LInkedList是双向链表
- 线程安全:Vector的方法被Synchronized修饰,所以是线程安全的;而ArrayList,Vector不是
- 效率:ArrayList,LinkedList效率高;Vector效率低
- ArrayList的改查效率较高,增删效率低下;LinkedList的增删效率高,改查效率低 下;
- 扩容机制:ArrayList是以1.5倍扩容;Vector是二倍扩容
30.JUC的了解:
- JUC是java.util.concurrent的缩写,这个包中包含了支持并发操作的各种工具
- 原子类:遵循比较和替换原则,可以用于解决单个变量的线程安全问题
- 锁:与Synchronized类似,在包括Synchronized所有功能的基础上,还支持超时机制,响应中断机制
- 线程池:可能更方便的管理线程,同时避免重复开线程和杀线程带来的消耗,效率高
- 并发容器:例如ConcurrentHashMap,支持多线程操作的并发集合,效率更快
31.HashMap的底层原理:
- 数据结构:jdk1.7之前“数组+链表”,jdk1.8之后“数组+链表+红黑树”
- put()流程:(基于哈希算法来确定元素位置)计算传入key的哈希值,并利用哈希值绝对值再余除集合长度来确定元素位置
- 如果这个位置,已经存在其他元素就发生哈希碰撞;HashMap使用链地址法解决哈希冲突,并且当链表长度为8时,链表会转化为红黑树,提高查询效率
- 扩容机制:数组的默认容量是16,会以2的指数倍进行扩容
- 线程安全:Hashmap非线程安全,在多环境下会产生循环死链;因此多线程环境下建议使用ConcurrentHashmap
32.说一下Java中的CAS:
- CAS( compare and swap )比较并交换,可以解决多线程并行情况下使用锁造成性能损耗的一种机制
- CAS属于乐观锁,CAS只能保证一个共享变量的原子操作
- CAS 操作包含三个操作数:1.内存位置,2.预期原值,3.新值
- 实现原理:当多个线程更新同一个变量时,若内存值和预期值相匹配,则更新该内存值为新值,若不等于会告知线程修改值失败不会挂起,但是可以重试
32.ConcurrentHashMap集合
- 支持大量并发访问的线程安全集合,jdk1.8之后底层和HashMap一样采用“数组+链表+红黑树”
- 采用锁定头节点的方式降低了锁粒度,以较低的性能实现了线程啊安全
33.ConcurrentHashMap在jdk1.7和1.8区别:
-
底层:
- jdk1.7底层是由Segment数组+ 多个HashEntry组成、Segment数组又是由多个Segment元素组成(默认16个),每个Segment元素又是由一个HashEntry数组和链表组成(每个Segment下就是一个HashMap) ;通过给每个Segment分段上Lock锁实现线程安全,锁的颗粒度为Segment
- jdk1.8底层是由Node数组 + 链表+ 红黑树,并发操作使用的是Synchronized和CAS来控制,锁的颗粒度为HashEntry
-
PUT方法:
-
jdk1.7会进行两次定位,先进行Segment定位再对其内部的Entry数组进行定位,定位后使用自旋锁进行加锁当自旋64次时进行膨胀,膨胀后线程进入阻塞状态,等待唤醒。整个put时都持有锁
jdk1.8只需要进行一次定位,使用synchronized+CAS机制,如对应下标没有节点,说明没有hash碰撞,使用cas进行插入,若成功则返回成功,失败则通过synchronized进行加锁
-
-
size计算方式:
-
jdk1.7先不加锁进行统计,最多统计三次,当前后两次值一样则返回,不一样则对每一个Segment进行加锁统计
jdk1.8会维护一个baseCount属性,每次put后会通过cas进行对baseCount自增
-
33.HashMap和HashTable的区别:
-
HashMap:
- 是非线程安全
- 允许存入null的,无论是以null作为key或value,都是可以的
-
HashTable:
- 是线程安全,性能不如HashMap
- 不允许存入null值,无论是以null作为key或value,都会引发异常
-
多线程环境下,不建议使用HashTable,建议使用java.util.Concurrent下的ConcurrentHashMap,它不但保证了线程安全,也通过降低锁的粒度提高了并发访问时的性能
38.HashSet和HashMap的区别:
-
相同点:HashSet和HashMap底层都是数组+链表+红黑树
-
不同点:
- 实现接口不同:HashMap实现了Map接口,HashSet实现了set接口
- 存储内容不同:HashMap存储的是键值对,HashSet存储的是对象
- 添加元素的方法不同:HashMap调用put方法,HashSet调用add方法
- 计算HashCode的方式不同:HashMap使用键Key来计算;HashSet使用成员对象来计算,如果HashCode相同,用equals来判断对象是否相同
- 效率:HashMap相对于HashSet较快
39.HashSet如何保证元素不重复:
- 和HashMap类似,在add添加元素时,计算Hash值,相同Hash值的元素使用equals方法进行比较,如果相同就替换原来的元素。
38.Java中常见的锁及原理:
- synchronized关键字:底层采用Java对象头来存储锁信息的
- Lock锁接口:基于AQS实现,AQS内部定义一个先进先出的队列实现锁的同步,同时还定义了同步状态来记录锁信息。
39.synchronized的用法及原理:
-
能够保证同一个时刻只有一个线程执行该代码,保证线程安全。在执行完或者出现异常时自动释放锁
-
用法:
- 静态方法:则锁是当前类的Class对象
- 普通方法:则锁是当前的实例(this)
- 代码块:则需要在关键字后面的小括号里,显式指定一个对象作为锁对象
-
原理:底层是采用Java对象头来存储锁信息的,并且还支持锁升级
40.什么时死锁:
- 两个或两个以上的线程在执行过程中,由于竞争资源或由于彼此通信而造成的一种堵塞的现象,若外力作用,它们都将无法推进下去,此时称系统处于死锁状态或产生了死锁,这些永远在互相等待的进程称为死锁进程。
40.死锁如何解决:
- 预防死锁:通过设置一些限制条件,去破坏产生死锁的必要条件
- 避免死锁:在资源分配过程中,使用某种方法避免系统进入不安全的状态,从而避免发生死锁
- 检测死锁:允许死锁的发生,但是通过系统的检测之后,采取一些措施,将死锁清除掉
- 解决死锁:该方法与检测死锁配合使用
40.说说你对 Volatile 的理解:
- volatile是 java 虚拟机提供的最轻量级的同步机制,volatile 主要提供修饰共享变量赋予 “可见性” 和 “有序性”
- volatile的功能就是被修饰的变量在被修改后可以立即同步到主内存
40.volatile和synchronized的区别:
- volatile本质是在告诉jvm变量需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住
- volatile只能修饰变量;synchronized可以修饰变量、实例方法,静态方法,代码块
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
41.synchronized和Lock有什么区别:
-
synchronized:是java关键字
-
Lock:lock是一个接口
-
使用方式:
-
synchronized:隐式锁,自动释放锁,在这种同步状态下,我们需要依赖Monitor(同步监视器)来实现线程通信
- 作用在静态方法上,类的Class对象
- 作用在实例方法上,当前实例(this)
- 作用在代码块上,则需要在关键字后面的小括号里显式指定一个对象作为Monitor
-
Lock接口是显示锁,手动释放锁,Lock接口具备更大的灵活性
-
-
功能特性:Lock弥补了synchronized不足,它新增特性包括:1.可中断地获取锁,2.非阻塞地获取锁,3.可超时地获取锁
-
实现机制:
- synchronized的底层是采用Java对象头来存储锁信息的
- lock通过trylock查看是否加锁成功
42.volatile的了解
- volatile关键字是比synchronized关键字更加轻量化,只能保证单共享变量的线程安全问题
- 保证变量的可见性和有序性,1.可见性:当一个共享变量被修改时,其他线程可以知道,2.有序性:通过禁止指令重排实现
40.AQS队列同步器:
- AQS是(AbstractQueuedSynchronizer)队列同步器,是用来构建锁的基础框架
- AQS是基于模板方法模式进行设计的,Lock实现类都是基于AQS实现的,所以锁的实现需要继承AQS并重写它的方法
- AQS内部定义了一个FIFO的队列来实现线程的同步,同时还定义了同步状态来记录锁的信息。
- 它的线程模式:独占和共享模式
43.BIO,NIO,O的了解:
- UNIX提供了5种I/O模型:1.阻塞I/O模型,2.非阻塞I/O模型,3.I/O复用模型,3.信号驱动I/O模型,异常I/O模型
- BIO:阻塞I/O模型(blocking I/O),是最常见的I/O模型,缺省情形下,所有文件操作都是阻塞的,一线程只能操作一个IO
- NIO:非阻塞I/O模型(nonblocking I/O),IO多路复用,一个线程可以同时处理多个IO请求,提供select,poll,epoll调用
- O:异步I/O模型(asynchronous I/O),告知内核启动某个操作,并让内核在整个操作完成后通知我们
44.Java NIO:
- NIO:非阻塞I/O模型(nonblocking I/O),IO多路复用,一个线程可以同时处理多个IO请求,提供select,poll,epoll调用
- NIO包含三大核心的组件:1.Buffer( 缓存区 ),2.Channel( 通道 ),3.Selector( 多路复用器 )
45.IO多路复用:
- IO多路复用:单个线程同时操作多个IO请求
- select调用:查询有多少个文件描述符需要进行IO操作,特点:1.轮询次数多,2.内存开销大,3.支持文件描述符的个数有限
- poll调用:和select几乎差不多,但是它的底层数据结构为链表,所以支持文件描述符的个数无上限
- epoll调用:更加高效的调用方式,底层的数据结构为红黑树加链表
46.static修饰符的用法:
- Java类中包含了成员变量,方法,构造器,初始化块和内部类(包括接口,枚举)5种成员
- static可修饰类,方法,代码块,变量
- 静态类:可以在不创建实例的情况下访问它的静态方法或者静态变量,而其实例方法或实例成员变量只能通过其实例对象来访问
- 静态方法:在静态方法不能使用this,因为this是随着对象创建而存在
- 静态成员变量:静态成员变量随着静态类的加载而创建
48.泛式和泛式擦除:
-
泛型的本质:参数化类型,即给类型指定一个参数
-
是JDK1.5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
-
泛式的格式:<引用数据类型>
-
泛式的使用:1.泛式接口,2.泛式类,3.泛式方法
public interface Data<E>{ ...... }public class MyArrayList<T>{ ..... }public <T> void show(T t){.....} -
泛式的好处:1.统一数据类型,2.可以在在编译时检查类型安全,3.所有的强制转换都是自动和隐式的,4.可以提高代码的重用率
-
泛式擦除:编译器在编译期间将我们写好的泛型进行擦除,并相应的做出一些类型转换
49.java泛型中extends 和 super的区别:
- 表示类型的上界,存储的类型是T或者T的子类型
- 表示类型下界,存储的类型是T或T的父类型
49.异常中有哪些个分类?:
- Java中Throwable是所有和异常的超类
- Throwable分为Error系统错误和Exception异常
- Error系统错误:1.虚拟机错误,2.内存溢出,3.线程死锁
- Exception异常分为:1.RuntimeException运行时异常,2.非运行时异常(编译异常)
- RuntimeException运行时异常:1.空指针异常,2.数组越界异常,3.算数异常,4.类型转换异常
- 非运行时异常:1.IO异常,2.SQL异常
49.NoClassDefFoundError和ClassNotFoundException区别:
-
NoClassDefFoundError:
- 是系统错误
- JVM在加载一个类的时候,如果这个类在编译阶段是可用的,但在运行时找不到这个类的定义时,JVM会抛出这个异常
-
ClassNotFoundException:
- 是异常
- 程序使用类加载加载Class文件时,系统按classpath找不到指定的类,会抛出这个异常
49.Java的异常处理机制:
- 异常处理机制可以让程序具有极好的容错性和健壮性,系统会生成一个Exception对象来通知程序
- 处理异常的语句由try,catch,finally三部分组成,try块用于包裹业务代码,catch块用于捕获并处理某一个类型的异常,finally块则用于回收资源
- 过程:如果业务代码发生异常,系统创建一个异常对象,并将其提交给JVM;由JVM寻找可以处理这个异常的catch块,并将异常对象交给这个catch块;如果JVM没有找到,运行环境终止,Java程序退出。
- Java也允许程序主动抛出异常。当业务代码中,判断某项错误的条件成立时,可以使用throw关键词向外抛出异常
50.throw和throws的区别
-
相同点:两者都是消极处理异常的方式,异常都由上层调用者处理,或者继续抛出
-
不同点:
- 位置不同:throw在方法内部;throws在方法签名处
- 内容不同:throw+异常对象,只能抛出一个异常,声明的是运行时异常;throws+异常的类型,可以声明多个异常,不一定会发生这些异常