Java基础
一、面向对象和面向过程的区别
⾯向过程 :⾯向过程性能⽐⾯向对象⾼。 因为类调⽤时需要实例化,开销⽐较⼤,⽐较消耗资源,所以当性能是最重要的考量因素的时候,⽐如单⽚机、嵌⼊式开发、Linux/Unix 等⼀般采⽤⾯向过程开发。但是,⾯向过程没有⾯向对象易维护、易复⽤、易扩展。
⾯向对象 :⾯向对象易维护、易复⽤、易扩展。 因为⾯向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,⾯向对象性能⽐⾯向过程低。
二、Java 语言有哪些特点?
简单易学;
⾯向对象(封装,继承,多态);
平台⽆关性( Java 虚拟机实现平台⽆关性);
可靠性;
安全性;
⽀持多线程;⽀持⽹络编程并且很⽅便
编译与解释并存;
三、关于 JVM JDK 和 JRE 最详细通俗的解答
Java 虚拟机(JVM)是运⾏ Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),⽬的是使⽤相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语⾔“⼀次编译,随处可以运⾏”的关键所在。
.java文件(源代码)-> JDK中javac编译 -> .class文件(java字节码)-> JVM -> 二进制机器码
JDK 是 Java Development Kit,它是功能⻬全的 Java SDK。它拥有 JRE 所拥有的⼀切,还有编译器(javac)和⼯具(如 javadoc 和 jdb)。它能够创建和编译程序。
JRE 是 Java 运⾏时环境。它是运⾏已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的⼀些基础构件。但是,它不能⽤于创建新程序。
四、Oracle JDK 和 OpenJDK 的对⽐
1、 Oracle JDK ⼤概每 6 个⽉发⼀次主要版本,⽽ OpenJDK 版本⼤概每三个⽉发布⼀次。但这不是固定的,我觉得了解这个没啥⽤处。
2、OpenJDK 是⼀个参考模型并且是完全开源的,⽽ Oracle JDK 是 OpenJDK 的⼀个实现,并不是完全开源的。
3、Oracle JDK ⽐ OpenJDK 更稳定。OpenJDK 和 Oracle JDK 的代码⼏乎相同,但 Oracle JDK 有更多的类和⼀些错误修复。
4、在响应性和 JVM 性能⽅⾯,Oracle JDK 与 OpenJDK 相⽐提供了更好的性能。
5、Oracle JDK 不会为即将发布的版本提供⻓期⽀持,⽤户每次都必须通过更新到最新版本获得⽀持来获取最新版本。
6、Oracle JDK 根据⼆进制代码许可协议获得许可,⽽ OpenJDK 根据 GPL v2 许可获得许可。
五、 Java 和 C++的区别?
1、都是⾯向对象的语⾔,都⽀持封装、继承和多态。
2、Java 不提供指针来直接访问内存,程序内存更加安全。
3、Java 的类是单继承的,C++ ⽀持多重继承;虽然 Java 的类不可以多继承,但是接⼝可以多继承。
4、Java 有⾃动内存管理机制,不需要程序员⼿动释放⽆⽤内存。
5、在 C 语⾔中,字符串或字符数组最后都会有⼀个额外的字符 ‘\0’ 来表示结束。但是,Java 语⾔中没有结束符这⼀概念。
六、什么是 Java 程序的主类 应用程序和小程序的主类有何不同?
⼀个程序中可以有多个类,但只能有⼀个类是主类。在 Java 应⽤程序中,这个主类是指包含 main()⽅法的类。⽽在 Java ⼩程序中,这个主类是⼀个继承⾃系统类 JApplet 或 Applet 的⼦类。应⽤程序的主类不⼀定要求是 public 类,但⼩程序的主类要求必须是 public 类。主类是 Java程序执⾏的⼊⼝点。
七、Java 应用程序与小程序之间有哪些差别?
简单说应⽤程序是从主线程启动(也就是 main() ⽅法)。applet ⼩程序没有 main() ⽅法,主要是嵌在浏览器⻚⾯上运⾏(调⽤ init() 或者 run() 来启动),嵌⼊浏览器这点跟 flash 的⼩游戏类似。
八、字符型常量和字符串常量的区别?
1、形式上: 字符常量是单引号引起的⼀个字符; 字符串常量是双引号引起的若⼲个字符。
2、含义上: 字符常量相当于⼀个整型值( ASCII 值), 可以参加表达式运算; 字符串常量代表⼀个地址值(该字符串在内存中存放位置)。
3、占内存⼤⼩:字符常量只占 2 个字节; 字符串常量占若⼲个字节 (注意: char 在 Java 中占两个字节)。
九、构造器 Constructor 是否可被 override?
Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个构造函数的情况。
十、重载和重写的区别
重载就是同⼀个类中多个同名⽅法根据不同的传参来执⾏不同的逻辑处理。
重写就是⼦类对⽗类⽅法的重新改造,外部样⼦不能改变,内部逻辑可以改变 。
重载发⽣在编译期,同⼀个类中。⽅法名必须相同,参数类型不同、个数不同、顺序不同,⽅法返回值和访问修饰符可以不同。
重写发⽣在运⾏期,是⼦类对⽗类的允许访问的⽅法的实现过程进⾏重新编写。返回值类型、⽅法名、参数列表必须相同,抛出的异常范围⼩于等于⽗类,访问修饰符范围⼤于等于⽗类。如果⽗类⽅法访问修饰符为 private / final / static,则⼦类就不能重写该⽅法,但是被 static 修饰的⽅法能够被再次声明。 构造⽅法⽆法被重写。
十一、Java 面向对象编程三⼤特性: 封装 继承 多态
封装把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的属性的⽅法,如果属性不想被外界访问,我们⼤可不必提供⽅法给外界访问。但是如果⼀个类没有提供给外界访问的⽅法,那么这个类也没有什么意义了。
继承是使⽤已存在的类的定义作为基础建⽴新类的技术,新类的定义可以增加新的数据或新的功能,也可以⽤⽗类的功能,但不能选择性地继承⽗类。通过使⽤继承我们能够⾮常⽅便地复⽤以前的代码。
多态就是指程序中定义的引⽤变量所指向的具体类型和通过该引⽤变量发出的⽅法调⽤在编程时并不确定,⽽是在程序运⾏期间才确定,即⼀个引⽤变量到底会指向哪个类的实例对象,该引⽤变量发出的⽅法调⽤到底是哪个类中实现的⽅法,必须在由程序运⾏期间才能决定。
十二、String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
String 类中使⽤ final 关键字修饰字符数组来保存字符串, private final charvalue[] ,所以 String 对象是不可变的。
⽽ StringBuilder 与 StringBuffer 都继承⾃ AbstractStringBuilder 类,在AbstractStringBuilder 中也是使⽤字符数组保存字符串 char[] value 但是没有⽤ final 关键字修饰,所以这两种对象都是可变的。
操作少量的数据: 适⽤ String
单线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuilder
多线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuffer
十三、自动装箱与拆箱
装箱:将基本类型⽤它们对应的引⽤类型包装起来;
拆箱:将包装类型转换为基本数据类型。
十四、在⼀个静态方法内调用⼀个非静态成员为什么是非法的?
由于静态⽅法可以不通过对象进⾏调⽤,因此在静态⽅法⾥,不能调⽤其他⾮静态变量,也不可以访问⾮静态变量成员。
十五、在 Java 中定义⼀个不做事且没有参数的构造方法的作用
Java 程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中“没有参数的构造⽅法”。因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找不到没有参数的构造⽅法可供执⾏。解决办法是在⽗类⾥加上⼀个不做事且没有参数的构造⽅法。
十六、 import java 和 javax 有什么区别?
刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使⽤。然⽽随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java包确实太麻烦了,最终会破坏⼀堆现有的代码。因此,最终决定 javax 包将成为标准 API 的⼀部分。
十七、接口和抽象类的区别是什么?
1、接⼝的⽅法默认是 public,所有⽅法在接⼝中不能有实现(Java 8 开始接⼝⽅法可以有默认实现),⽽抽象类可以有⾮抽象的⽅法。
2、接⼝中除了 static、final 变量,不能有其他变量,⽽抽象类中则不⼀定。
3、⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝⾃⼰本身可以通过 extends 关键字扩展多个接⼝。
4、接⼝⽅法默认修饰符是 public,抽象⽅法可以有 public、protected 和 default 这些修饰符(抽象⽅法就是为了被重写所以不能使⽤ private 关键字修饰!)。
5、从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计,⽽接⼝是对⾏为的抽象,是⼀种⾏为的规范。
十八、成员变量与局部变量的区别有哪些?
1、从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数;成员变量可以被 public,private,static 等修饰符所修饰,⽽局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
2、从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存对象的引⽤或者是指向常量池中的地址。
3、从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失。
4、成员变量如果没有被赋初值:则会⾃动以类型的默认值⽽赋值(⼀种情况例外:被 final 修饰的成员变量也必须显式地赋值),⽽局部变量则不会⾃动赋值。
十九、创建⼀个对象用什么运算符?对象实体与对象引用有何不同?
new 运算符,new 创建对象实例(对象实例在堆内存中),对象引⽤指向对象实例(对象引⽤存放在栈内存中)。⼀个对象引⽤可以指向 0 个或 1 个对象(⼀根绳⼦可以不系⽓球,也可以系⼀个⽓球);
⼀个对象可以有 n 个引⽤指向它(可以⽤ n 条绳⼦系住⼀个⽓球)。
二十、什么是方法的返回值?返回值在类的方法里的作用是什么?
⽅法的返回值是指我们获取到的某个⽅法体中的代码执⾏后产⽣的结果!(前提是该⽅法可能产⽣结果)。返回值的作⽤:接收出结果,使得它可以⽤于其他的操作!
二十一、一个类的构造方法的作用是什么? 若⼀个类没有声明构造方法,该程序能正确执行吗? 为什么?
主要作⽤是完成对类对象的初始化⼯作。可以执⾏。因为⼀个类即使没有声明构造⽅法也会有默认的不带参数的构造⽅法。
二十二、构造方法有哪些特性?
1、名字与类名相同。
2、没有返回值,但不能⽤ void 声明构造函数。
3、⽣成类的对象时⾃动执⾏,⽆需调⽤。
二十三、静态方法和实例方法有何不同
1、在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅式。⽽实例⽅法只有后⾯这种⽅式。也就是说,调⽤静态⽅法可以⽆需创建对象。
2、静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态⽅法),⽽不允许访问实例成员变量和实例⽅法;实例⽅法则⽆此限制。
二十四、对象的相等与指向他们的引用相等,两者有什么不同?
对象的相等,比较的是内存中存放的内容是否相等。⽽引⽤相等,比较的是他们指向的内存地址是否相等。
二十五、在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
帮助⼦类做初始化⼯作。
二十六、== 与 equals(重要)
== :它的作⽤是判断两个对象的地址是不是相等。即,判断两个对象是不是同⼀个对象 ( 基本数据类型 == 比较的是值,引⽤数据类型 == 比较的是内存地址)。
equals() : 它的作⽤也是判断两个对象是否相等。但它⼀般有两种使⽤情况:
情况 1:类没有覆盖 equals() ⽅法。则通过 equals() 比较该类的两个对象时,等价于通过 == 比较这两个对象。
情况 2:类覆盖了 equals() ⽅法。⼀般,我们都覆盖 equals() ⽅法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
二十七、hashCode 与 equals (重要)
hashCode() 的作⽤就是获取哈希码,也称为散列码;它实际上是返回⼀个 int 整数。这个哈希码的作⽤是确定该对象在哈希表中的索引位置。 hashCode() 在散列表中才有⽤,在其它情况下没⽤。在散列表中 hashCode() 的作⽤是获取对象的散列码,进⽽确定该对象在散列表中的位置。
1、如果两个对象相等,则 hashcode ⼀定也是相同的。
2、两个对象相等,对两个对象分别调⽤ equals ⽅法都返回 true。
3、两个对象有相同的 hashcode 值,它们也不⼀定是相等的。
4、因此,equals ⽅法被覆盖过,则 hashCode ⽅法也必须被覆盖。
5、hashCode() 的默认⾏为是对堆上的对象产⽣独特值。如果没有重写 hashCode(),则该 class的两个对象⽆论如何都不会相等。
二十八、为什么 Java 中只有值传递?
二十九、简述线程、程序、进程的基本概念。以及他们之间关系是什么?
线程与进程相似,但线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执⾏的过程中可以产⽣多个线程。与进程不同的是同类的多个线程共享同⼀块内存空间和⼀组系统资源,所以系统在产⽣⼀个线程,或是在各个线程之间作切换⼯作时,负担要⽐进程⼩得多,也正因为如此,线程也被称为轻量级进程。
程序是含有指令和数据的⽂件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。
进程是程序的⼀次执⾏过程,是系统运⾏程序的基本单位,因此进程是动态的。系统运⾏⼀个程序即是⼀个进程从创建,运⾏到消亡的过程。简单来说,⼀个进程就是⼀个执⾏中的程序,它在计算机中⼀个指令接着⼀个指令地执⾏着,同时,每个进程还占有某些系统资源如 CPU 时间,内存空间,⽂件,输⼊输出设备的使⽤权等等。换句话说,当程序在执⾏时,将会被操作系统载⼊内存中。 线程是进程划分成的更⼩的运⾏单位。线程和进程最⼤的不同在于基本上各进程是独⽴的,⽽各线程则不⼀定,因为同⼀进程中的线程极有可能会相互影响。从另⼀⻆度来说,进程属于操作系统的范畴,主要是同⼀段时间内,可以同时执⾏⼀个以上的程序,⽽线程则是在同⼀程序内⼏乎同时执⾏⼀个以上的程序段。
三十、线程有哪些基本状态?
线程创建之后它将处于 NEW(新建) 状态,调⽤ start() ⽅法后开始运⾏,线程这时候处于 READY(可运⾏) 状态。可运⾏状态的线程获得了 cpu 时间⽚(timeslice)后就处于 RUNNING(运⾏) 状态。
当线程执⾏ wait() ⽅法之后,线程进⼊ WAITING(等待)状态。进⼊等待状态的线程需要依靠其他线程的通知才能够返回到运⾏状态,⽽ TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,⽐如通过 sleep(long millis) ⽅法或 wait(long millis) ⽅法可以将 Java线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调⽤同步⽅法时,在没有获取到锁的情况下,线程将会进⼊到 BLOCKED(阻塞) 状态。线程在执⾏Runnable 的 run() ⽅法之后将会进⼊到 TERMINATED(终⽌) 状态。
三十一、关于 final 关键字的⼀些总结
final 关键字主要⽤在三个地⽅:变量、⽅法、类。
1、对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。
2、当⽤ final 修饰⼀个类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指定为 final ⽅法。
3、使⽤ final ⽅法的原因有两个。第⼀个原因是把⽅法锁定,以防任何继承类修改它的含义;第⼆个原因是效率。在早期的 Java 实现版本中,会将 final ⽅法转为内嵌调⽤。但是如果⽅法过于庞⼤,可能看不到内嵌调⽤带来的任何性能提升(现在的 Java 版本已经不需要使⽤ final⽅法进⾏这些优化了)。类中所有的 private ⽅法都隐式地指定为 final。
三十二、Java 中的异常处理
在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。Throwable: 有两个重要的⼦类:Exception(异常) 和 Error(错误) ,⼆者都是 Java 异常处理的重要⼦类,各⾃都包含⼤量⼦类。
Error(错误):是程序⽆法处理的错误,表示运⾏应⽤程序中较严重问题。⼤多数错误与代码编写者执⾏的操作⽆关,⽽表示代码运⾏时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运⾏错误(Virtual MachineError),当 JVM 不再有继续执⾏操作所需的内存资源时,将出现OutOfMemoryError。这些异常发⽣时,Java 虚拟机(JVM)⼀般会选择线程终⽌。
Exception(异常):是程序本身可以处理的异常。Exception 类有⼀个重要的⼦类 RuntimeException。RuntimeException 异常由 Java 虚拟机抛出。NullPointerException(要访问的变量没有引⽤任何对象时,抛出该异常)、ArithmeticException(算术运算异常,⼀个整数除以 0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
异常处理总结
try 块: ⽤于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟⼀个finally 块。
catch 块: ⽤于处理 try 捕获到的异常。
finally 块: ⽆论是否捕获或处理异常,finally 块⾥的语句都会被执⾏。当在 try 块或catch 块中遇到 return 语句时,finally 语句块将在⽅法返回之前被执⾏。
在以下 4 种特殊情况下,finally 块不会被执⾏:
1、在 finally 语句块第⼀⾏发⽣了异常。 因为在其他⾏,finally 块还是会得到执⾏
2、在前⾯的代码中⽤了 System.exit(int)已退出程序。 exit 是带参函数 ;若该语句在异常语句之后,finally 会执⾏
3、程序所在的线程死亡。
4、关闭 CPU。
三十三、Java 序列化中如果有些字段不想进行序列化,怎么办?
对于不想进⾏序列化的变量,使⽤ transient 关键字修饰。
transient 关键字的作⽤是:阻⽌实例中那些⽤此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和⽅法。
三十四、获取用键盘输入常用的两种方法
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
三十五、Java 中 IO 流
Java 中 IO 流分为⼏种?
按照流的流向分,可以分为输⼊流和输出流;
按照操作单元划分,可以分为字节流和字符流;
按照流的⻆⾊划分,可以分为节点流和处理流。
BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写⼊必须阻塞在⼀个线程内等待其完成。在活动连接数不是特别⾼(⼩于单机 1000)的情况下,这种模型是⽐不错的,可以让每⼀个连接专注于⾃⼰的 I/O 并且编程模型简单,也不⽤过多考虑系统的过载、限流等问题。
NIO (Non-blocking/New I/O): NIO 是⼀种同步⾮阻塞的 I/O 模型,在 Java 1.4 中引⼊了NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它⽀持⾯向缓冲的,基于通道的 I/O 操作⽅法。 NIO提供了与传统 BIO 模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和ServerSocketChannel 两种不同的套接字通道实现,两种通道都⽀持阻塞和⾮阻塞两种模式。
AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引⼊了 NIO 的改进版 NIO 2,它是异步⾮阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直接返回,不会堵塞在那⾥,当后台处理完成,操作系统会通知相应的线程进⾏后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在⽹络操作中,提供了⾮阻塞的⽅法,但是 NIO 的 IO ⾏为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程⾃⾏进⾏ IO 操作,IO 操作本身是同步的。
三十六、常见关键字总结: static,final,this,super
三十七、Collections ⼯具类和 Arrays 工具类常见方法总结
三十八、深拷贝 vs 浅拷贝
1、浅拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型进⾏引⽤传递般的拷⻉,此为浅拷⻉。
2、深拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容,此为深拷⻉。