1、JDK、JRE、JVM 分别是什么关系?
JDK 即为 Java 开发工具包,包含编写 Java 程序所必须的编译、运行等开发工具以及 JRE。开发工具如: 用于编译 Java 程序的 javac 命令。 用于启动 JVM 运行 Java 程序的 Java 命令。 用于生成文档的 Javadoc 命令。 用于打包的 jar 命令等等。
2、为什么 Java 被称作是“平台无关的编程语言”?
Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。 Java 源文件( `.java` )被编译成能被 Java 虚拟机执行的字节码文件( `.class` )。 Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
3、Java 和 C++ 的区别?
都是面向对象的语言,都支持封装、继承和多态。 Java 不提供指针来直接访问内存,程序内存更加安全。 Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。 Java 有自动内存管理机制,不需要程序员手动释放无用内存。
4、什么是字节码?采用字节码的最大好处是什么?
Java 中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。 编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在 Java 中,这种供虚拟机理解的代码叫做字节码(即扩展名为 `.class` 的文件),它不面向任何特定的处理器,只面向虚拟机。 每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java 源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。这也就是解释了 Java 的编译与解释并存的特点。 采用字节码的好处: Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以 Java 程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。
5、Java运行的过程?
Java 源代码=> 编译器 => JVM 可执行的 Java 字节码(即虚拟指令)=> JVM => JVM 中解释器 => 机器可执行的二进制机器码 => 程序运行
6、Java是动态类型语言还是静态类型语言?
动态类型语言和静态类型语言两者的却别就在于对类型的检查是在编译期还是在运行期,满足前者就是静态类型语言,反之则是动态类型语言。 直白来说静态语言是判断变量自身的类型信息;动态类型语言是判断变量值的类型信息,变量设有类型信息,变量值才有类型信息,这是动态语言的一个重要特征。 Java是静态类型语言(尽管lambda表达式为其增加了动态特性),js,python是动态类型语言。
7.什么是面向对象?
面向对象是一种思想,世间万物都可以看做一个对象,这里只讨论面向对象编程(OOP),Java 是一个支持并发、基于类和面向对象的计算机高级编程语言。面向对象软件开发具有以下优点: 代码开发模块化,更易维护和修改。 代码复用性强。 增加代码的可读性。
8.请说说面向对象的特征?封装、继承、多态分别说一下,以及它们的好处?
1.封装:封装,给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改变它内部的数据。在 Java 当中,有 4 种修饰符: `default`、`public`、`private` 和 `protected` 。每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。 使用封装的一些好处: 通过隐藏对象的属性来保护对象内部的状态。 提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。 禁止对象之间的不良交互提高模块化。 2.继承:继承,给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行,也可以在不修改类的情况下给现存的类添加新特性。 3.多态:多态,是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上的操作,可以应用到其他类型的值上面。 多态中,父类作为形参的方法和子类作为形参的方法都是一样的。形参父类类型可以接收子类对象。这是多态的特性。 重要特性:转型 向上转型:父类引用指向子类对象。相当于子类对象赋给父类引用。这样子,是默认的。子类的方法可以重写父类的方法。但是,向上转型的弊端就是只能使用父类有的方法,一旦向上转型就不能调用子类特有的方法。所以需要向下转型还原。 向下转型:其实是一个还原的步骤。子类引用指向父类的一个引用。这个要强转类型。因为我们父类不能去拿子类已经有的东西。所以必须把父类转化为和子类一个类型。
9.多态为什么要转型?
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。
10.说一下权限修饰符的使用权限?
同一个类:四个都能访问。 同一个包不同类:public、protected和defaut都能访问。private不能访问 不同包但是B类是A类的子类: 首先,必须导包。如果要用B类继承A类,必须先导入A类所在的包。 public和protect能访问。default不能访问。private也不能。 不同包非子类:一点关系没有。两个类是陌生关系。不是一个包里的也没有继承(实现)关系。 只有public可以访问。其他都不行。
基本数据类型和引用类型
11.Java 中的几种基本数据类型是什么?各自占用多少字节?
基本数据类型如下: byte 1字节 boolean false/true(理论上占用1bit,1/8字节,实际处理按1byte处理) char 2字节(C语言中是1字节) short 2字节 int 4字节 long 8字节 float 4字节 double 8字节
12.char 型变量中能不能存贮一个中文汉字?为什么?
在 Java 语言中,char 类型占 2 个字节,而且 Java 默认采用 Unicode 编码,一个 Unicode 码是 16 位,所以一个 Unicode 码占两个字节。 Java 中无论汉字还是英文字母,都是用 Unicode 编码来表示的。所以,在 Java 中,char 类型变量可以存储一个中文汉字。
13.什么是引用类型?
引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。 引用类型包括类、接口、数组等。 特别注意,String 是引用类型不是基本类型。
14.什么是值传递和引用传递?
值传递,是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。 引用传递,一般是对于对象型变量而言的,传递的是该对象地址的一个副本,并不是原对象本身。 一般认为,Java 内的传递都是值传递,Java 中实例对象的传递是引用传递。
15.属性(字段)和变量的区别?
字段是成员变量,是在类中声明的变量。 变量可以分为局部变量和成员变量。成员变量就是字段也叫做属性。 所以字段就是属性就是成员变量。
16.什么是重载和重写?Overload(重载)和Override(重写)的区别?
重载:Overload就是重载的意思。 重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。 Overload对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,JVM就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点: 1、在使用重载时只能通过不同的参数样式。 例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int)); 2、不能通过访问权限、返回类型、抛出的异常进行重载; 3、方法的异常类型和数目不会对重载造成影响; 4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。 重写:Override是覆盖的意思,也就是重写。 重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。 子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。 override可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点: 1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果; 2、覆盖的方法的返回值必须和被覆盖的方法的返回一致; 3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类; 4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
17.什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用?
我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象,例如,要将java对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个java对象变成某个格式的字节流再传输。 但是,jre本身就提供了这种支持,我们可以调用OutputStream的writeObject方法来做,如果要让java帮我们做,要被传输的对象必须实现serializable接口,这样,javac编译时就会进行特殊处理,编译的类才可以被writeObject方法操作,这就是所谓的序列化。需要被序列化的类必须实现Serializable接口,该接口是一个mini接口,其中没有需要实现方法,implements Serializable只是为了标注该对象是可被序列化的。 例如,在web开发中,如果对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬盘,这个对象就必须实现Serializable接口。如果对象要经过分布式系统进行网络传输,被传输的对象就必须实现Serializable接口。
字符串相关
18.String 为什么是不可变的?
String 类中使用 `final` 关键字字符数组保存字符串。 String源码: // String.java private final char[] value; 并且它是在堆中的字符串常量池中的,所以不可变。 StringBuilder 与 StringBuffer为什么是可变的? StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 `char[] value` ,但是没有用 `final` 关键字修饰。 源码: // AbstractStringBuilder.java char[] value; 所以这两种对象都是可变的。
19.String s = new String("xyz") 会创建几个对象?
两个。你new一个,在堆中创建了一个对象。 还有一个在字符串常量池中的字符串对象,它指向了一个字节数组。字节数组会把"xyz"转化为相对应的ascii码存储在字节数组里面。
20.说说你对字符串常量池的理解?
1.从jdk1.7开始,字符串常量池在堆当中。 2.字符串底层就有一个字节数组。名叫value。字符串常量池中的对象保存的就是byte[]字节数组的地址值。指向了字节数组。但是字符串对象自己也有一个地址。 3.jvm在帮你创建字符串abc的时候,其实是把它化成一个字节数组,再把字节数组的地址保存在字符串常量池中的字符串对象的地址中,再把字符串对象的地址赋给你创建的堆中的字符串引用。也就是说这个引用是先指向了字符串常量池中的字符串对象,字符串对象再指向字节数组,然后获取值。 4.如果再创建一个字符串的话,也是指向同一个内存地址。但是值不同。 5.所有的String都指向一个地址。除非你去new一个,那就是不同的。 若String s1 = “hello1”,同时声明String s2 = s1,则s1和s2现在指向同一个地址,该地址指向字符串内容hello1 若在说明1的基础上,再声明s1 = “hello1”,则同样s1和s2指向同一个地址,该地址指向字符串内容内容hello1 若在说明1的基础上,再声明s1 = “hello2”,则出现了变化,s1和s2的地址不再相同 若现在重新声明String s2 = new String(s1),则说明s1和s2现在不同(s1==s2为false),但是所指向的内容是一致的,也就是说它们再堆内存中的地址是不一样的,但是它们都指向了相同的字节数组 总结:因为String类是不可变的(imutable),当修改一个字符串时,我们不是在原来字符串的基础上进行修改,而是申请一个新的地址空间,并将内容写在新的空间里。
21.String、StringBuffer、StringBuilder 的区别?
Java 平台提供了两种类型的字符串:String 和 StringBuffer/StringBuilder,它们可以储存和操作字符串。 String:只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。StringBuffer/StringBuilder 类,表示的字符串对象可以直接进行修改。每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。 StringBuilder:StringBuilder 是 Java 5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被 `synchronized` 修饰,因此它的效率也比 StringBuffer 要高。 StringBuffer:StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。 相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。 对于三者使用的总结: 操作少量的数据 = String 。这个也是实际编码较为经常使用的方式。 单线程操作字符串缓冲区下操作大量数据 = StringBuilder 。甚至有时,我们为了避免每个线程重复创建 StringBuilder 对象,会通过 ThreadLocal + StringBuilder 的方式,进行对 StringBuilder 的重用。 多线程操作字符串缓冲区下操作大量数据 = StringBuffer。实际场景下,我们基本不太会出现,多线程操作同一个 StringBuffer 对象。
关键字相关
22.谈谈你对this关键字的理解?
this关键字是存放在java栈帧中局部变量表变量槽的索引0的位置。它指的是一个方法或者变量的引用,通常指的是当前方法或者变量。一个对象去调用另一个方法,或者一个对象去调用一个变量,就是把指针指向了这个方法或者变量所处的局部变量表中的this指针的位置。也就是索引为0的位置。随后这个方法就进栈。 this的三种用法 1.调用属性:this可以调用本类中的任何成员变量 2.调用方法(可省略):this调用本类中的成员方法(在main方法里面没有办法通过this调用) 3.调用构造方法:this调用构造方法只能在本构造方法中调用另一个构造方法;this 调用构造方法必须写在第一行
23.谈谈你对super关键字的理解?
super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。 super的三种用法: 1.在子类的成员方法中,访问父类的成员变量。 2.在子类的成员方法中,访问父类的成员方法。 3.在子类的构造方法中,访问父类的构造方法
24.那你知道this和super有什么区别吗?
this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。 super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
25.谈谈你对static关键字的理解?
只要是用了static关键字的变量,就不再属于对象,是属于类的。共享。 被static关键字修饰的变量,随着类加载的出现而出现。 它在类加载过程的时候在链接阶段的准备阶段为类的静态变量分配内存,并将其初始值转化为默认值0或者null。在初始化阶段为类的静态变量赋予正确的初始值,就是你所定义的那个初始值 它优先于对象出现,所以在静态环境中不可以访问非静态变量或者方法。 如果你的代码尝试不用实例来访问非 `static` 的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
26.谈谈你对final关键字的理解?
final关键字代表最终、不可改变的的意思。 常见四种用法: 1.可以用来修饰一个类 2.可以用来修饰一个方法 3.可以用来修饰一个局部变量 4.还可以用来修饰一个成员变量 final不能和abstract同时使用。因为矛盾。 abstract必须有子类来继承重写方法,否则类没有意义。但是final修饰的类不能有子类,矛盾。 就跟static和this矛盾一样。
哈希算法
27.你知道在Java中都有哪里用到了哈希算法吗?
1.HashMap.hash(),侧重点是速度
2.Object.hashCode(),直接获取内存地址
3.Integer.hashCode(),直接返回的int类型的Value
4.String.hashCode(),根据字符串内容生成hashCode,字符串内容一样则hashCode也相同
拷贝相关
28.Java中有多少种拷贝类型?分别说一下?
浅拷贝:浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化。仅仅只是复制引用地址,而不是新建一个内存。 在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。 深拷贝:深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变。新建了一个新的内存 最重要的是将引用类型成员变量复制出来,相当于在内存中开辟了一块新的地址了。新地址里面的值和旧的地址里面的值一样。但是因为是在新的地址里面操作了所以我们无论怎么操作都不会改变旧的地址里面的东西。两个地址是完全独立的。 在Java语言中,如果需要实现深克隆,可以通过覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。
29.那你碰到过多引用拷贝吗?知道怎么解决吗?
解决多引用拷贝的方式: 如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。 序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。 Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。
接口相关
30.你平时是怎么使用接口的?谈谈你的理解?
1.接口没有静态代码块或者构造方法。 2.一个类的直接父类是唯一的,但是一个类可以实现多个接口。 3.一个类同时实现了两个接口,两个接口的方法重名了,那么只需覆盖重写一次就好。 4.如果实现类没有覆盖重写所有接口中的所有方法,那么实现类就必须是一个抽象类。必须要有子类继承这个实现类重写完抽象方法这个接口体系才合法。 5.如果实现类所实现的多个接口中存在重复的默认方法,那么实现类一定要对冲突的默认方法进行重写。 6.继承和实现的优先级问题:当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执 行父类的成员方法。 7.接口的多继承:一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。而且要带着default关键字。 也就是说,这个接口无法继承使用它们父接口中的同名默认方法,因为冲突了。 但是如果不是默认方法,是普通方法,那么继承谁的都行。因为都是一样的,实现的也是一样的功能。 但是默认方法不行,因为默认方法是相当于在接口中就对方法进行了实现。这样会导致冲突。
31.那听你这么说,接口的实现类的方法返回值类型是什么?
返回值类型可以是接口、也可以是接口的实现类引用、或者直接new一个实现类对象也行。
32.方法的参数可以是接口吗?
当然不行。如果方法形参类型是接口的话,传的参必须是其实现类或者new,不然怎么能叫引用呢。参数都是引用。 但是如果形参类型是类的话,形参可以是这个类的子类或者直接new一个子类。因为是继承。参数是引用,只要能指向对象就行。
33.接口的返回值类型可否是另一个接口类型?
可以。一个接口里可以让方法的返回值类型是其他接口的类型。其实就是玩多态。
34.接口引用调用实现类方法?
只能调用实现类重写的。实现类自己的不能调用。
抽象类
35.抽象类使用规则
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类能够实现完剩下的全部抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。 如果A是抽象类,B继承A,必须得继承全部的。否则B也是个抽象类,不能创建对象。C去继承B的话,得继承完剩下的B没继承A的,如果方法B已经继承,可以重写,但是要等到C重写了所有没实现的抽象类,才可以创建对象,去调用方法来使用。否则,C依然是个抽象类。
36.抽象类注意事项?
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
37.abstractclass和interface语法上有什么区别?
1.抽象类可以有构造方法,接口中不能有构造方法。 2.抽象类中可以有普通成员变量,接口中没有普通成员变量 3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。 4. 抽象类中可以包含静态方法,接口中不能包含静态方法。 5. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是publicstatic final类型,并且默认即为publicstatic final类型。 6.一个类可以实现多个接口,但只能继承一个抽象类。
38.接口是否可继承接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?
接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的main方法。 只要记住:抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。
包装类
39.什么是自动拆装箱?
自动装箱和拆箱,就是基本类型和引用类型之间的转换。
40.为什么要转换?
因为不能直接地向集合( Collection )中放入原始类型值,因为集合只接收对象。 通常这种情况下你的做法是,将这些原始类型的值转换成对象,然后将这些转换的对象放入集合中。使用 Integer、Double、Boolean 等这些类,我们可以将原始类型值转换成对应的对象,但是从某些程度可能使得代码不是那么简洁精炼。 为了让代码简练,Java5 引入了具有在原始类型和对象类型自动转换的装箱和拆箱机制。
泛型
41.你平时是如何使用泛型的?
1.使用含有泛型的类: 在类上标注泛型代表我们还不知道是什么类型。等到测试类创建对象的时候确定泛型的类型。到时候类和方法的类型会变成相对应的类型。 2.使用含有泛型的方法: 在调用方法的时候确定泛型的类型。 3.使用含有泛型的接口: 第一种方式,创建泛型接口,接口上未确认泛型是什么类型。在实现类上决定泛型的类型。并且之后的所有实现方法都会默认是这个类型。 第二种方式,接口已经确认了泛型类型。实现类就得跟着用什么类型。如果一个子接口继承了父接口,那么父接口如果确定了泛型类型,子接口也得跟着是什么类型。 4.使用泛型通配符 ?:代表任意的数据类型使用方式: 但它不能创建对象使用,只能作为方法的参数使用。
异常
42.Exeception和Error区别?
Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。 Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。 Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。 Exception 又分为可检查(checked)异常和不可检查(unchecked)异常: 可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是 Throwable 不是 Exception。 不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。
43.那你知道异常有几种处理方式吗?
1.throws 交给别人处理。步骤: 只要我们在方法里面throw抛出一个异常,如果是RuntimeException或者其子类,就会默认给jvm接盘处理。jvm会中断程序,打印到控制台上。是jvm进行中断处理的 否则,我们要在方法体throws上声明,然后给下一个接盘者。 如果是在主方法调用,因为主方法调用了它,所以主方法要么处理异常,要么就继续在主方法名上throws找人接盘处理,最后会让jvm来处理。 所以,只要throw了,至少要在异常的方法名上throws,要么自己处理,要么找人接盘。 2.try catch finally 自己处理异常,方式: 多个异常多次抛出多次处理 多个异常一次捕获,一次处理 多个异常一次抛出多次处理 运行时异常可以不用抛出和捕获处理,交给jvm
44.说说Throwable类怎么用吧?
Throwable类中有3个异常处理方法 1.getMessage():返回throwable的简短信息 2.toString():返回throwable的详细消息字符串 3.printStackTrace():jvm打印异常对象,默认此方法,异常信息最全面
45.你最常见到的是Java中的什么异常?
那必须得是运行时异常啊: ClassCastException(类转换异常) IndexOutOfBoundsException(数组越界异常) NullPointerException(空指针异常) ArrayStoreException(数据存储异常,操作数组时类型不一致) BufferOverflowException(还有IO操作的,缓冲溢出异常)
反射机制
46.什么是反射机制?
Class类对象阶段是在内存中的。已经被编译成字节码加载进虚拟机内存。这个时候对于我们用户来说是看不到类的一些信息的,那我们如果又想看到这些类的信息,就可以通过反射机制获取,或者说当你无法确定到底传入的类是什么样子的时候,要通用兼容所有可能的类,就需要用反射机制动态去调用类的属性和行为。
47.那怎么使用反射呢?
1.使用反射获取字节码Class对象: Class.forName("全类名") Class.forName("全类名") 对象.getClass() 2.使用Class对象: 通过Field类获取成员变量 通过Constructor类获取构造方法 通过Method类获取成员方法
48.你反射这么熟悉,那一般在哪些场景用到它啊?
1.逆向代码 ,例如反编译 2.与注解相结合的框架 3.动态获取信息,动态代理(AOP中的动态代理:实现了接口的使用JDK的动态代理,没有实现接口的使用CGlib动态代理)使用反射
49.听你说的这么多,那它就没有缺点吗?
有的。它的性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,所以性能比直接的java代码要慢很多。
JDK1.8新特性
50.你知道JDK1.8有哪些新特性吗?
1.函数式接口 函数式接口指的是有且只有一个抽象方法的接口,并且接口中可以包含其它的方法。 我们可以使用@FunctionalInterface注解去检测一个接口是否是一个函数式接口 常用的函数式接口有: Supplier接口:它是一个生产型接口,当你指定接口是什么类型时,接口的get方法就会生产什么类型的数据 Function接口:这个接口用来根据一个类型的数据去获取另一个类型的数据,前者称为前置条件,后者称为后置条件。 这个接口中最主要的抽象方法为:R apply(T t),去根据类型T的参数获取类型R的结果。 使用场景有很多,比如:将String类型转化为Integer类型。 2.Stream流: Stream流属于管道流,只能波消费(使用)一次 第一个Stream流调用完毕方法,数据就会流转到下一个Stream上而这时第一个Stream流已经使用完毕,就会关闭了所以第一个Stream流就不能再调用方法了。 常用方法: forEach():用来遍历流中的数据,是一个终结方法。遍历之后就不能再调用Stream流中的其它方法了。 filter():它可以传递lambda表达式,对数据进行过滤。 map()::该接口需要一个Function函数式接口参数,可以将当前流中的T数据转换为另一种R类型的流。 skip():如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流。 concat():如果有两个流希望合并成一个流,那么使用Stream接口的静态方法concat就可以 3.Lambda表达式: lambda表达式的标准格式由三部分组成: 1.箭头 2.参数 3.代码 格式: (参数列表) -> {核心代码}; 格式说明: () :接口中抽象方法的参数列表,没有参数的时候就空着,有参数的话就正常写出参数,多个参数用逗号分割 -> :传递的意思,把参数传递给方法体{} {} :重写接口的抽象方法的方法体 对于Lambda表达式来说,真正重要的是参数,方法体和返回值。其他的,都可以省略。