Java核心知识经典面试题来啦(基础语法篇)

基础语法篇

一、Java的特点有哪些

Java语言是一种分布式的面向对象语言,具有面向对象平台无关性简单性解释执行多线程安全性等很多特点。

1.面向对象

Java是一种面向对象的语言,它对对象中的对象继承封装多态、接口以及等均有很好的支持。为了简单起见,Java只支持类之间的单继承,但是可以使用接口来实现多继承

2.平台无关性(在编译阶段体现)

平台无关性的具体表现在于,Java是Write Once, Run any Where的语言。Java语言使用Java虚拟机机制屏蔽了具体平台的相关信息,使得Java语言编译的程序只需生成虚拟机上的目标代码(即将Java语言编译为.class[字节码文件]),就可以在不同平台上不加修改地运行(JVM实现将字节码文件编译为机器码)

3.简单性

Java舍弃了很多C++中难以理解的特性,如操作符的重载多继承等,而且Java语言不使用指针,加入了垃圾回收机制,解决了程序员需要管理内存的问题,使编程变得更加简单

4.解释执行

Java程序在Java平台运行时会被转化为.class文件字节码[.class就是可以到处运行的文件],然后可以在有Java环境(JVM)的操作系统上运行。在运行文件时,Java的解释器会对这些字节码进行解释执行,进行过程中需要加入的类在连接阶段被载入到运行环境中

5.多线程

Java支持多个线程同时执行,并提供多线程之间的同步机制。每一个线程都有自己的run()方法,要执行的方法就写在run()方法体内。

6.分布式

Java语言支持Internet应用的开发,而且Java有一个丰富的例程库,用于处理像**TCP/IPFTP之类的TCP/IP协议,包括URLURLConnetionSocket**等。Java的RIM机制也是开发分布式应用重要手段。

7.健壮性

Java的强类型机制异常处理垃圾回收机制等都是Java健壮性的重要保证,并且Java采用的指针模型可以消除重写内存和损坏数据的可能性,异常机制也是健壮性的一大体现。

8.安全性

Java通常被用在网络环境中,为此,Java提供了一个安全机制以防止恶意代码的攻击。除了Java语言具有许多安全特性外,Java还对网络下载的类增加一个安全防范机制,分配不同的名字空间以防止替代本地的同名类,并包含安全管理机制。


二、JDK、JRE和JVM的区别

1.JDK

JDK(Java SE Development Kit): Java标准的开发包,提供了编辑运行Java程序所需要的各种工具和资源,包括了Java编译器Java运行时环境,以及常用的Java类库

2.JRE

JRE(Java Runtime Environment):Java运行时环境,用于解释执行Java的字节码文件(即将.class文件翻译为机器码)。如果只是简单的运行Java程序,只安装JRE即可。

3.JVM

JVM(Java Virtual Mechinal), Java虚拟机,是JRE的一部分。它是Java实现跨平台的核心,负责解释执行字节码文件,是可运行Java字节码文件的虚拟计算机。所有平台上的JVM向编译器提供相同的接口,而编译器只需要面向虚拟机,生成虚拟机可识别的字节码。

关系

JDK包括JRE,JRE包括JVM。Java编译器编译Java程序时,生成的是于平台无关的字节码文件,JVM是运行字节码文件的虚拟机。

为什么要采用字节码

在Java中,JVM可以理解的代码就叫做字节码[.class文件](Java源代码经过编译器编译后的文件),它只面向虚拟机。

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点,因此Java运行时比较高效,而且字节码并不针对某一种特定机器,只要该机器上安装了JVM即可运行。

什么是跨平台性

所谓跨平台性:是指Java语言编写的程序,一次编译后,可以在多个系统平台上运行。

实现原理:Java程序通过JVM在系统平台上运行,只要该系统安装了JVM即可运行Java程序。

到处运行的关键和前提就是JVM。因为第二次编译中JVM起着关键作用,在可以运行Java虚拟机的地方都内含着一个JVM操作系统,从而使Java提供了各种不同平台上的虚拟机制,因此实现了到处运行的效果**(Java不是编译机制,而是解释机制**)

Java程序从编译到运行

从源代码到运行: 编码 => 编译 => 运行 (编译过程体现了Java跨平台的特点)

首先将Java源代码转化成.class文件字节码(JDK中的javac编译),.class文件就是可以到处运行的文件。然后Java字节码会被转化为机器码,这是由JVM来执行的,即Java的第二次编译。


三、面向对象特性

  • 封装

    利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能的隐藏内部的细节,只保留一些对外接口使之于外部发生联系。用户无需知道对象内部的细节,可以通过对象对外提供的接口来访问该对象。

    优点

    • 耦合性低:可以独立的开发、测试、优化、使用、理解和修改
    • 减轻维护负担:可以更容易被程序员理解,并且在调试的时候可以不影响其他模块
    • 有效调节性能:可以通过刨析确定哪些模块影响了系统性能
    • 提高软件可重用性
    • 降低了构建大型系统的风险:即使整个系统不可用,但是这些独立模块有可能可用
  • 继承

    继承实现了IS-A关系,例如Dog和Animal就是一种IS-A关系,因此Cat可以继承自Animal,从而获得Animal非private的属性和方法。

    继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。

    Dog可以当作Animal来用,也就是说可以使用Animal来引用Dog对象。父类引用指向子类对象称为向上转型

    Eg: Animal dog = new Dog()

  • 多态

    • 编译时多态:主要指方法的重载

    • 运行时多态:指程序中定义的对象引用所指向的具体类型在运行期间才确定

      运行时多态有三个条件:

      继承 覆盖(重写) 向上转型


四、a = a + b 与 a += b 的区别

两个整型(byte、short、int),在进行运算时会自动提升为int类型

+=操作符会隐式进行自动类型转换,即将操作的结果类型自动转换为持有结果的类型,而a = a + b不会自动进行类型转换

byte a = 127;
byte b = 127;
b = a + b;  //cannot convert from int to byte 因为此时b的类型仍为byte
b += a;    //ok  此时为b的类型为int

再来看一个例子~,该代码是否有错

short s = 1;
s = s + 1;

有错误,short类型在进行运算时会自动提升为int类型,也就是说s + 1的结果为int类型,但是s仍为short类型

s += 1;    //+=操作符会对右边的表达式结果强转匹配左边的数据类型 左边为short

五、Java中的自动装箱与拆箱

理解自动装箱拆箱

自动装箱、拆箱实际上是一种语法糖。可以简单理解为Java平台为我们自动做了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的

装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型

原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建10万个对象和10万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。

Integer i = 101;  //自动装箱 调用对应包装类的valueOf方法  此处为Integer.valueOf();

int x = i;		  //自动拆箱 调用对应包装类的intValue方法    此处为x.intValue()
了解了自动装箱与拆箱,来看看下面这段代码吧~
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;

//试问i1 == i2 与 i3 == i4的结果    true  |  false
为什么结果会不一样呢?

在提供valueOf方法创建Integer对象时,如果数值在[-128, 127]之间,便返回IntegerCache.cache中已存在的对象引用,否则则创建一个新的Integer对象

i1i2返回的是同一个对象,而i3i4则分别指向不同的对象

再战一城
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;

//同样的问题  i1 == i2 与 i3 == i4的结果   均为false

因为在某个范围内浮点数个数是没有限制的,即100.0后面还有若干位小数,故任意两个Double类型的对象均不等

为什么要有包装类型?

为了让基本类型也具有对象的特征,就出现了包装类型。比如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型。因为容器都是装Object的,这时就需要这些基本类型的包装类了。

  • 基本数据类型与Java泛型不能配合使用
  • 无法高效的表达数据,也不便于表达复杂的数据结构,如vector和tuple
缓存机制在自动装箱时是否起作用?

Integer.valueOf() => IntegerCache.cache

Boolean 缓存了true/false对应实例,确切的说,只会返回两个常量实例Boolean.TRUE/FALSE

Short:与Integer相同,[-128, 127]

Bye:数值有限,全部被缓存

Character:缓存范围’\u0000’到’\u007F’

基本类型与包装类型的区别
  1. 声明方式:基本类型不使用new关键字,而包装类型需要用new关键字来在堆中分配存储空间
  2. 存储方式及位置不同:基本类型是将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用
  3. 初始值不同:基本类型的初始值,如int为0, boolean为false,而包装类型为null
  4. 使用方式不同:基本类型直接赋值使用就好,而包装类型在集合如CollectionMap时会使用到

六、String、StringBuffer、StringBulider有什么区别

String

String是Java语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的Immutable类,被声明为final class,所有属性也都是final的(即不可变)。由于它的不可变性,类似拼接、裁剪字符串等操作,都会产生新的String对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能也有一定影响。

String是不可变的有什么好处

1.缓存hash值,因为String的hash值会经常用到,比如String做HashMap的key,那么String不可变保证hash值不可变,只需要计算一次,提高计算效率

2.String Pool 字符串常量池,一旦字符串String 被创建,下次创建相同的字符串就可以从常量池直接取,也只有当String是不可变才能这样做

3.安全String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。

4.线程安全 String是不可变的,保证了线程的安全。

StringBuffer

StringBuffer是为解决String拼接产生太多对象问题而提供的一个类,我们可以用append或者add方法,把字符串添加到已有序列的末尾或指定位置。StringBuffer本质是一个线程安全的可修改字符序列,它保证了线程安全,也随之带来了额外的性能开销,StringBuffer线程安全的实现是通过把各种修改数据的方法都加上synchonized

StringBuilder

StringBuilder是Java1.5中新增的,在能力上它和StringBuffer没有本质区别,但是他去掉了线程安全的部分,有效减小了开销,除非有线程安全的需要,它便是字符串拼接的首选

StringImmutable类的典型实现,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,并且由于不可变,Immutable对象在拷贝时不需要额外复制数据。

为了实现修改字符序列的目的,StringBufferStringBuilder底层都是利用可修改的(char JDK9以后是byte)数组,二者都继承了AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了synchronized

七、final、finally、finalize有什么不同

final可以用来修饰类、方法、变量,分别有不同的意义,final修饰的类代表不可以继承扩展,final的变量是不可以改变的,final的方法也是不可以重写的。

finally则是Java保证重点代码一定要被执行的一种机制。我们可以使用tyr-finally或者try-catch-finally来进行类似JDBC关闭连接,保证unlock锁等动作

finalize是基础类java.lang.Object的一个方法,他的设计目的是保证对象在被垃圾收集前完成特定资源的回收(现在已经不推荐使用)

finalize本是上成为了快速回收的阻碍者

  • finalize被设计成在对象垃圾收集前被调用,这就意味着finalize方法的对象是个”特殊公民“,JVM要对它进行额外处理。
  • 我们回收资源就是因为资源都是有限的,垃圾收集时间不可预测,可能会极大加剧资源占用。
  • finalize还会掩盖资源回收时的出错信息,也就意味着一旦出现异常或者出错,我们得不到任何有效信息

八、接口和抽象类有什么区别

  • 继承/实现:一个类只能继承一个抽象类,但能实现多个接口
  • 构造方法:抽象类可以,接口没有
  • 普通成员变量:抽象类中可以有普通成员变量,接口中没有
  • 静态~:接口和抽象类中都可以有静态成员变量,抽象类中访问修饰符任意,接口只能public static final(默认)
  • 普通方法:抽象类中可以不含抽象方法,可以有普通方法;接口在JDK8之前都是抽象方法,在JDK8可以有default方法,在JDK9中允许有private普通方法
  • 静态方法:抽象类可以有,接口在JDK8之前不能有,在JDK8中可以有静态方法且只能被接口类直接调用
  • 方法:抽象类中的方法可以是publicprotected;接口方法在JDK8之前只有public abstract,在JDK8可以有default方法,在JDK9中允许有private方法

接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到API定义与实现分离的目的。

抽象类不能实例化,主要用于代码重用,大多抽取相关Java类的公用方法或者共同成员变量,然后通过继承的方式达到代码复用的目的

九、强引用、软引用、弱引用、幻象引用(虚引用)有什么区别

不同的引用类型,主要体现的是对象不同的可达性状态和对垃圾收集的影响

  • 强引用:最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还活着,垃圾收集器就不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将(强)引用赋值为null,就可以被当垃圾收集了。
  • 软引用:相对强引用稍微弱一些,可以让对象豁免一些垃圾收集,只有当JVM认为内存不足时才会收回软引用指向的对象,JVM会确保在OOM之前,清理软引用指向的对象。软引用通常指向内存敏感的缓存,如果还有空闲就可以暂时保留,内存不足时清理掉。
  • 弱引用:不能使对象豁免垃圾收集,仅提供一种访问在弱引用下对象的途径。这就可以用来构建一种没有特点约束的关系,如维护一种非强制性的映射关系,如果视图获取时,对象还在就使用它,否则实现实例化。
  • 虚引用:不能通过它访问对象。它仅提供了一种确保对象被finalize后,做某些事情的机制,比如可以利用幻象引用监控对象的创建和销毁。

十、动态代理是基于什么原理?

反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省(introspect)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时调用

动态代理时一种方便运行时动态构建代理动态处理代理方法调用的机制,很多场景都是利用类似机制做的,比如面向切面编程(AOP)

实现动态代理的方式有很多,如JDK自身提供的动态代理,就是主要利用的上面提到的反射机制。还有更高性能的字节码操作机制(cglib)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值