转载

Java 基础(理论篇)

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、面向对象的特征有哪些方面?

  1. 封装 :通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
  2. 继承 :继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
  3. 抽象 :抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
  4. 多态性 :多态性是指允许不同子类型的对象对同一消息作出不同的响应。即同一消息可以根据发送对象的不同而采取不同的行为方式。
  • 多态性分为编译时的多态性和运行时的多态性。
  • 方法重载(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 变量?

因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以直接访问非静态的成员。

最常见的 static 方法就是 main,作为程序的入口,所有对象都是在该方法里面实例化。

10、Java 支持的数据类型有哪些?什么是自动拆装箱?

  • 基本数据类型:byte,short,char,int,long,float,double,boolean。
  • 引用数据类型:Integer、String 等。引用类型实体在堆中,声明的变量存放的是该对象的引用地址。

自动装箱就是 Java 编译器在基本数据类型和对应的对象包装类型间的转化,即 int 转化为 Integer,自动拆箱是 Integer 调用其方法将其转化为 int 的过程

11、Java 中的方法覆盖(Overriding)和方法重载(Overload)是什么意思?

方法重写

  • 方法名,参数列表必须相同,返回类型可以相同也可以是原类型的子类型
  • 重写方法不能比原方法访问性差(即访问权限不允许缩小)。
  • 重写方法不能比原方法抛出更多的异常。
  • 重写发生在子类和父类之间
  • 重写实现运行时的多态性

方法重载

  • 方法名必须相同,参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)
  • 方法的返回类型可以相同也可以不相同。
  • 重载发生在同一类中
  • 重载实现编译时的多态性

12、Java 中,什么是构造方法?什么是构造方法重载?什么是复制构造方法?

Java 中的构造函数是为了初始化对象的,构造函数的函数名和类名一致,默认的构造函数没有参数,没有返回值,构造函数的函数体内,没有内容。 构造函数的重载是函数名与类名相同,参数列表不同。同样也是为了初始化对象的。

关于复制构造函数:C++ 中的复制构造函数通常有三种作用

  1. 对象作为函数参数
  2. 对象作为函数返回值
  3. 使用一个对象对另一个对象初始化。

C++ 语法允许用户定义自己的复制构造函数以实现自定义的复制,比如说进行 深复制 。Java 并不支持这样的复制构造函数。但是这并不代表 Java 中没有这种机制,在 Java 中 Object 类的 clone()方法就是这种机制的体现。而且通过以上三种方式对 Java 对象进行的操作都是对引用的操作,不像 C++ 里面是对原对象的操作,因此 Java 中也不需要考虑需要使用复制构造函数这种问题。

13、Java支持多继承么?

Java 中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是 Java 中的接口支持多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。

14、什么是值传递和引用传递?

值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量. 引用传递一般是对于引用型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。一般认为,Java 内的基础类型数据传递都是值传递. Java 中实例对象的传递是引用传递

Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。

当基本型变量作为参数传递到方法中时,参数的值是该变量的副本,改变副本不影响原变量。

StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
    builder = new StringBuilder("ipad");
}
foo(sb); // sb 没有被改变,还是 "iphone"。
复制代码

推荐阅读: Java中只有按值传递,没有按引用传递!

15、== 和 equals 的区别是什么?

基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean 它们之间的比较,应用双等号(==),比较的是它们的值。

复合数据类型(类)。当它们用双等号进行比较的时候,比较的是它们在内存中的存放地址,所以,除非是同一个 new 出来的对象,它们的比较后的结果为 true,否则比较后结果为 false。 Java 当中所有的类都是继承于 Object 这个基类的,在 Object 中的基类中定义了一个 equals 的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如 String,Integer,Date 在这些类当中 equals 有其自身的实现(在重写 equals 方法的时候,有必要重写对象的 hashCode 方法,从而保证程序完整性),而不再是比较类在堆内存中的存放地址了。

16、为什么重写 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 确实不相同的。

17、final 在 Java 中有什么作用?

  1. 当用 final 修饰一个类时,表明这个类不能被继承。
  2. final 修饰的方法不能被重写。
  3. final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

18、java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有: StringStringBufferStringBuilder

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

19、如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

20、String 类的常用方法都有那些?

indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较

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、接口和抽象类的区别和相同点是什么?

接口和抽象类的区别

  1. 从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
  2. 定义接口的关键字是 interface ,抽象类的关键字是 abstract class
  3. 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
  4. 类可以实现很多个接口,但是只能继承一个抽象类
  5. Java 接口中声明的变量默认都是 final 的。抽象类可以包含非 final 的变量。
  6. Java 接口中的成员函数默认是 public 的。抽象类的成员函数可以是 private,protected 或者是 public。

相同点:

  1. 接口是绝对抽象的,不可以被实例化,抽象类也不可以被实例化。
  2. 类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。

24、普通类和抽象类有哪些区别?

普通类不能包含抽象方法,抽象类可以包含抽象方法。

抽象类不能直接实例化,普通类可以直接实例化。

25、抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被 synchronized 修饰?

都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

27、Java 有没有 goto?

goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。

28、&和&&的区别?

&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是 true 整个表达式的值才是 true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

29、Java 中 IO 流分为几种?

  • 按功能来分:输入流(input)、输出流(output)。
  • 按类型来分:字节流和字符流。
  • 按照流的角色划分为节点流和处理流。

字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。

节点流:可以从或向一个特定的地方(节点)读写数据。如 FileReader。处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

推荐阅读: JAVA的节点流和处理流

Java IO 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。

  • InputStream/Reader : 所有输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer : 所有输出流的基类,前者是字节输出流,后者是字符输出流。

按操作方式分类结构图:

Java 基础(理论篇)

按操作对象分类结构图:

Java 基础(理论篇)

30、BIO、NIO、AIO 有什么区别?

  • BIO: Block IO 同步阻塞式 IO ,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。

  • 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 的非阻塞模式来开发。

  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,在 Java 7 中引入了 NIO 的改进版 NIO 2,它是 异步非阻塞的IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。

31、Files的常用方法都有哪些?

  • Files.exists():检测文件路径是否存在。
  • Files.createFile():创建文件。
  • Files.createDirectory():创建文件夹。
  • Files.delete():删除一个文件或目录。
  • Files.copy():复制文件。
  • Files.move():移动文件。
  • Files.size():查看文件个数。
  • Files.read():读取文件。
  • Files.write():写入文件。
原文  https://juejin.im/post/5dcea9a96fb9a02020457a46
正文到此结束
Loading...