1、面向对象和面向过程的区别
面向过程:面向过程性能比面向对象高。因为对象调用需要实例化,开销比较大,较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等,一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
面向对象:面向对象易维护、易复用、易扩展。因为面向对象有封装、继承、多态性的特性,所以可设计出低耦合的系统,使得系统更加灵活、更加易于维护。
面向过程性能比面向对象高的背后原因?
面向过程也需要分配内存,计算内存偏移量,Java 性能差的主要原因并不是因为它是面向对象语言,而是因为 Java 是半编译语言,最终的执行代码并不是可以直接被 CPU 执行的二进制机器码。
而面向过程语言大多都是直接编译成机器码在电脑上执行,并且其它一些面向过程的脚本语言性能也并不一定比 Java 好。
2、Java 语言有哪些特点?
- 简单易学(其实相比较 Python 还是更容易上手一些)
- 面向对象(封装、继承、抽象、多态)
- 平台无关性(Java 虚拟机实现字节码文件的跨平台)
- 安全可靠性
- 支持多线程(C++语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)
- 支持网络编程并且很方便(Java 语言诞生就是为了简化网络编程设计的)
- 编译与解释并存
扩展:为什么说 Java 语言编译与解释并存?
当 .class 字节码文件通过 JVM 转为机器可以执行的二进制机器码时,JVM 类加载器首先加载字节码文件,然后通过解释器逐行进行解释执行,这种方式的执行速度相对比较慢。而且有些方法和代码块是反复被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成一次编译后,会将字节码对应的机器码保存下来,下次可以直接调用。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言。
HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9引入了一种新的编译模式AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销。JDK支持分层编译和AOT协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。
3、什么是 Java 虚拟机?为什么 Java 被称作是“平台无关的编程语言”?
Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件。
Java 的跨平台指的是 java 源文件经过 javac 编译器编译成的二进制.class 字节码的跨平台性。各个平台装有不同的 jvm,而 jvm 能将相同的字节码翻译成平台相关的机器码,进而执行。
4、JDK 和 JRE 的区别是什么?
Java 运行时环境(JRE)。它包括 Java 虚拟机、Java 核心类库和支持文件。它不包含开发工具(JDK)--编译器、调试 和其他工具。
Java 开发工具包(JDK)是完整的 Java 软件开发包,包含了 JRE,编译器和其他的工具(比如:JavaDoc,Java 调试器),可以让开发者开发、编译、执行 Java 应用程序。
5、Java 和 C++的区别?
- 都是面向对象的语言,都支持封装、继承和多态;
- Java 不提供指针来直接访问内存,程序内存更加安全;
- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承;
- Java 有自动内存管理机制,不需要程序员手动释放无用内存。
6、面向对象的特征有哪些方面?
- 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
- 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
- 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
- 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。即同一消息可以根据发送对象的不同而采取不同的行为方式。
- 多态性分为编译时的多态性和运行时的多态性。
- 方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。
- 实现多态的技术:动态绑定,是指执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
- 实现多态的条件:(1)继承(2)方法重写(3)父类引用指向子类对象。
- 多态的作用:消除类型之间的耦合关系。
7、访问修饰符 public,private,protected,以及不写(默认)时的区别?
private:类成员只能被当前类本身访问,如果类的构造方法声明为 private,则其他类不能生成该类的实例。default:类成员可以被这个类和同一个包中的类所访问。protected:类成员被同一个类,同一个包中的其他类,子类(同包或不同包)访问。public:被所有类访问。
8、”static”关键字是什么意思?Java 中是否可以覆盖(override)一个 private 或者是 static 的方法?
static 表示静态的意义,可以修饰成员变量、成员方法、内部类和代码块,被 static 修饰的变量叫做静态变量,随类加载一次,可以被多个对象共享。被其修饰的方法称为静态方法(类方法),其随着类的加载而被加载,可以通过类直接访问,不需要实例化对象。此外静态方法只能访问静态成员,不可以直接访问非静态成员。静态内部类可以直接调用静态构造器(不用对象)。静态代码块只需加载一次,可以有多个代码块。
重写是子类中的方法和子类继承的父类中的方法一样(函数名,参数,参数类型,返回值类型),但是子类中的访问权限要不低于父类中的访问权限。重写的前提是必须要继承,private 修饰不支持继承,因此被私有的方法不可以被重写。静态方法形式上可以被重写,但是会被隐藏。原因在于方法重写基于运行时动态绑定,而 static 方法是编译时静态绑定的。
9、static内容扩展
static 修饰的内容在继承关系中的加载顺序如下图所示:
关于静态代码块、构造代码块、构造函数的初始化顺序:
静态代码块:用staitc声明,jvm加载类时执行,仅执行一次 构造代码块:类中直接用{}定义,每一次创建对象时执行。 执行顺序优先级:静态块,构造块,构造方法。 复制代码
关于静态域的初始化顺序:
public class Test2 { public static Test2 test = new Test2(); { System.out.println("子类构造代码块"); } static { System.out.println("子类静态代码块"); } public static Test2 test2 = new Test2(); public Test2(){ System.out.println("子类构造方法"); } public static void main(String[] args) { Test2 test2 = new Test2(); } } 复制代码
执行结果为:
子类构造代码块 子类构造方法 子类静态代码块 子类构造代码块 子类构造方法 子类构造代码块 子类构造方法 复制代码
10、是否可以在 static 环境中访问非 static 变量?
因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以直接访问非静态的成员。
最常见的 static 方法就是 main,作为程序的入口,所有对象都是在该方法里面实例化。
11、成员变量与局部变量的区别有哪些?
- 从语法形式上看:成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public、private、static 等修饰符所修饰,而局部变量不能被这些修饰符所修饰;但是它们都可以被 final 所修饰。
- 从变量在内存中的存储方式来看:如果成员变量被 static 所修饰,那么这个成员变量属于类,如果没有被 static 修饰,则该成员变量属于对象实例。对象存在于堆内存,局部变量存在于栈内存(具体是Java虚拟机栈)。
- 从变量在内存中的生存时间来看:成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用结束而自动消失。
- 成员变量如果没有赋初始值,则会自动以类型的默认值而赋值(例外:被 final 修饰的成员变量必须在初始化时赋值),局部变量则不会自动赋值。
12、Java 支持的数据类型有哪些?什么是自动拆装箱?
- 基本数据类型:byte,short,char,int,long,float,double,boolean。
- 引用数据类型:Integer、String 等。引用类型实体在堆中,声明的变量存放的是该对象的引用地址。
自动装箱就是 Java 编译器在基本数据类型和对应的对象包装类型间的转化,即 int 转化为 Integer,自动拆箱是 Integer 调用其方法将其转化为 int 的过程。
13、Java 中的方法覆盖(Overriding)和方法重载(Overload)是什么意思?
方法重写(两同两小一大)
- 方法名,参数列表必须相同,返回类型可以相同也可以是原类型的子类型
- 重写方法不能比原方法访问性差(即访问权限不允许缩小)。
- 重写方法不能比原方法抛出更多的异常。
- 重写发生在子类和父类之间
- 重写实现运行时的多态性
方法重载
- 方法名必须相同,参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)
- 方法的返回类型可以相同也可以不相同。
- 重载发生在同一类中
- 重载实现编译时的多态性
14、Java 中,什么是构造方法?什么是构造方法重载?什么是复制构造方法?
Java 中的构造函数是为了初始化对象的,构造函数的函数名和类名一致,默认的构造函数没有参数,没有返回值,构造函数的函数体内,没有内容。 构造函数的重载是函数名与类名相同,参数列表不同。同样也是为了初始化对象的。
关于复制构造函数:C++ 中的复制构造函数通常有三种作用
- 对象作为函数参数
- 对象作为函数返回值
- 使用一个对象对另一个对象初始化。
C++ 语法允许用户定义自己的复制构造函数以实现自定义的复制,比如说进行深复制。Java 并不支持这样的复制构造函数。但是这并不代表 Java 中没有这种机制,在 Java 中 Object 类的 clone()方法就是这种机制的体现。而且通过以上三种方式对 Java 对象进行的操作都是对引用的操作,不像 C++ 里面是对原对象的操作,因此 Java 中也不需要考虑需要使用复制构造函数这种问题。
15、Java支持多继承么?
Java 中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是 Java 中的接口支持多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。
16、什么是值传递和引用传递?
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量. 引用传递一般是对于引用型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。一般认为,Java 内的基础类型数据传递都是值传递. Java 中实例对象的传递是引用传递
Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。
当基本型变量作为参数传递到方法中时,参数的值是该变量的副本,改变副本不影响原变量。
StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder = new StringBuilder("ipad"); } foo(sb); // sb 没有被改变,还是 "iphone"。 复制代码
推荐阅读:Java中只有按值传递,没有按引用传递!
17、== 和 equals 的区别是什么?
基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean 它们之间的比较,应用双等号(==),比较的是它们的值。
复合数据类型(类)。当它们用双等号进行比较的时候,比较的是它们在内存中的存放地址,所以,除非是同一个 new 出来的对象,它们的比较后的结果为 true,否则比较后结果为 false。 Java 当中所有的类都是继承于 Object 这个基类的,在 Object 中的基类中定义了一个 equals 的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如 String,Integer,Date 在这些类当中 equals 有其自身的实现(在重写 equals 方法的时候,有必要重写对象的 hashCode 方法,从而保证程序完整性),而不再是比较类在堆内存中的存放地址了。
18、为什么重写 equals 时必须重写 hashCode 方法?
hashCode()与equals()的相关规定:
- 如果两个对象相等(即用 equals 比较返回 true),则 hashcode 一定也是相同的;
- 两个对象有相同的 hashcode 值,它们也不一定是相等的(不同的对象也可能产生相同的 hashcode,概率性问题);
- equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。
为什么要有 hashCode?
我们先以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode: 当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相同的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
通过我们可以看出:hashCode() 的作用就是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
继续上面的话题,为什么必须要重写 hashcode 方法,其实简单的说就是为了保证同一个对象,保证在 equals 相同的情况下 hashcode 值必定相同,如果重写了 equals 而未重写 hashcode 方法,可能就会出现两个没有关系的对象 equals 相同的(因为 equals 都是根据对象的特征进行重写的),但 hashcode 确实不相同的。
19、final 在 Java 中有什么作用?
- 当用 final 修饰一个类时,表明这个类不能被继承。
- final 修饰的方法不能被重写。
- final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
20、java 中操作字符串都有哪些类?它们之间有什么区别?
操作字符串的类有:String
、StringBuffer
、StringBuilder
。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
21、int和Integer有什么区别?
public class Test03 { public static void main(String[] args) { Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println(f1 == f2); System.out.println(f3 == f4); } //结果 true false 复制代码
Java 在编译 Integer i = 100 ;时,会翻译成为 Integer i = Integer.valueOf(100)。根据 Java API 中对 Integer 类型的 valueOf 的定义可知,当数值在[-128,127]范围内时,Integer 对象会存储在缓存中,f1 和 f2 都指向的是缓存中同一个对象,而不会 new 个新对象。反之如果不在该范围内,f3 和 f4 指向两个不同的对象。
22、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?
Static Nested Class 是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
class Outer { class Inner {} public static void foo() { new Inner(); //编译报错 } public void bar() { new Inner(); } public static void main(String[] args) { new Inner();//编译报错 } } 复制代码
Java 中非静态内部类对象的创建要依赖其外部类对象,上述代码中 foo 和 main 方法都是静态方法,静态方法中没有 this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做:new Outer().new Inner();
23、关于内部类的总结
24、接口和抽象类的区别和相同点是什么?
接口和抽象类的区别
- 从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
- 定义接口的关键字是 interface ,抽象类的关键字是 abstract class
- 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
- 类可以实现很多个接口,但是只能继承一个抽象类,接口可以继承多个接口
- Java 接口中声明的变量默认都是 public static final 的。抽象类可以包含非 final 的变量。
- 在JDK1.8之前,接口中不能有静态方法,抽象类中可以有普通方法和静态方法;在 JDK1.8后,接口中可以有默认方法和静态方法,并且有方法体。
- 抽象类可以有构造方法,但是不能直接被 new 关键字实例化。
- 在 JDK1.8 前,抽象类的抽象方法默认访问权限为 protected,1.8默认访问权限为 default,共有 default,protected 、 public 三种修饰符,非抽象方法可以使用四种修饰符;在 JDK1.8 前,接口方法默认为 public,1.8时默认为 public,此时可以使用 public 和 default,1.9时接口方法还支持 private。
相同点:
- 接口是绝对抽象的,不可以被实例化,抽象类也不可以被实例化。
- 类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
25、抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被 synchronized 修饰?
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
27、Java 有没有 goto?
goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。
扩展:null、true、false 是关键字吗?
true
和false
不是关键字,而是布尔文字null
不是关键字,而是 null 文字- Java9 之后增加了下划线(_)作为关键字
官方文档: docs.oracle.com/javase/spec…
28、&和&&的区别?
&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是 true 整个表达式的值才是 true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
29、Java 中 IO 流分为几种?
- 按功能来分:输入流(input)、输出流(output)。
- 按类型来分:字节流和字符流。
- 按照流的角色划分为节点流和处理流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
节点流:可以从或向一个特定的地方(节点)读写数据。如 FileReader。处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
推荐阅读:JAVA的节点流和处理流
Java IO 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
- InputStream/Reader: 所有输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作方式分类结构图:
按操作对象分类结构图:
30、BIO、NIO、AIO 有什么区别?
1、BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
传统的服务器端同步阻塞I/O处理(也就是BIO,Blocking I/O)的经典编程模型 :
{ ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//线程池 ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(8088); while(!Thread.currentThread.isInturrupted()){//主线程死循环等待新连接到来 Socket socket = serverSocket.accept(); executor.submit(new ConnectIOnHandler(socket));//为新的连接创建新的线程 } class ConnectIOnHandler extends Thread{ private Socket socket; public ConnectIOnHandler(Socket socket){ this.socket = socket; } public void run(){ while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){死循环处理读写事件 String someThing = socket.read()....//读取数据 if(someThing!=null){ ......//处理数据 socket.write()....//写数据 } } } } 复制代码
2、NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。
3、AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。
31、获取用键盘输⼊常用的两种⽅法
1、通过 Scanner
Scanner scanner = new Scanner(System.in); String value = scanner.nextLine(); System.out.println(value); scanner.close(); 复制代码
2、通过 BufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); try { String name = reader.readLine(); reader.close(); } catch (IOException e) { e.printStackTrace(); } 复制代码
32、Java8 高级特性
1.Lambda表达式
Java Lambda表达式的一个重要用法是简化某些匿名内部类(Anonymous Classes
)的写法。实际上 Lambda 表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。
2.Stream函数式操作流元素集合
3.接口新增:默认方法与静态方法
Java 8 新增了接口的默认实现,通过 default 关键字表示。同时也可以提供静态默认方法。
4.方法引用,与Lambda表达式联合使用
通过方法引用,可以使用方法的名字来指向一个方法。使用一对冒号来引 "::" 用方法。
5.引入重复注解
6.类型注解
7.最新的Date/Time API (JSR 310)
8.新增base64加解密API
9.数组并行(parallel)操作
10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
推荐阅读:聊聊 Java8 以后各个版本的新特性