备战11月JAVA面试:整合JAVA面试题,共同努力(持续上传)【第一天】

160 阅读18分钟
前段时间在弄毕业设计,也是有一段时间没有更新。最近比较轻松,利用这段时间Damon也是好好的整理一些笔试题。每天大概整理十条,毕竟太多大家也没有耐心去看,同时我自己也要好好准备,等到这边实训结束之后,也要开始找实习了。让我们一起努力,共同找到心仪的office吧。

1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?

Java虚拟机是一个可以执行Java字节码的虚拟进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。

Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者重新编译。因为Java源码经过jvm编译后得到class字节码,运行在jvm上,然后经过jvm将class文件解释称对应平台的机器码执行。所以Java程序被称作为“平台无关的编程语言”,也被叫做“一处编译,处处运行”。Java虚拟机让这变成可能,因为它知道底层硬件平台的指令长度和其他特性。

2.JDK和JRE的区别是什么?

JRE: Java Runtime Environment

JDK:Java Development Kit

JRE顾名思义是java运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。

JDK顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。

如果你需要运行java程序,只需安装JRE就可以了。如果你需要编写java程序,需要安装JDK。

JRE根据不同操作系统(如:windows,linux等)和不同JRE提供商(IBM,ORACLE等)有很多版本。

再说说java的跨平台吧:

java源程序先经过javac编译器编译成二进制的.class字节码文件(java的跨平台指的就是.class字节码文件的跨平台,.class字节码文件是与平台无关的),.class文件再运行在jvm上,java解释器(jvm的一部分)会将其解释成对应平台的机器码执行,所以java所谓的跨平台就是在不同平台上安装了不同的jvm,而在不同平台上生成的.class文件都是一样的,而.class文件再由对应平台的jvm解释成对应平台的机器码执行

最后解释下机器码和字节码的区别:

  • 机器码,完全依附硬件而存在~并且不同硬件由于内嵌指令集不同,即使相同的0 1代码 意思也可能是不同的~换句话说,根本不存在跨平台性~比如~不同型号的CPU,你给他个指令10001101,他们可能会解析为不同的结果~
  • 我们知道JAVA是跨平台的,为什么呢?因为他有一个jvm,不论那种硬件,只要你装有jvm,那么他就认识这个JAVA字节码,至于底层的机器码,咱不用管,有jvm搞定,他会把字节码再翻译成所在机器认识的机器码。

3:”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

static关键字,static原意是“静态的”。

  • ①static可以修饰内部类,但是不能修饰普通类。静态内部类的话可以直接调用静态构造器(不用对象)。

  • ②static修饰方法, static 方法就是没有 this 的方法。在 static 方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用 static 方法。这实际上正是 static 方法的主要用途。 方便在没有创建对象的情况下来进行调用(方法/变量)。最常见的static方法就是main,因为所有对象都是在该方法里面实例化的,而main是程序入口,所以要通过类名来调用。还有就是main中需要经常访问随类加载的成员变量。

  • ③static修饰变量,就变成了静态变量,随类加载一次,可以被多个对象共享。

  • ④static修饰代码块,形成静态代码块,用来优化程序性能,将需要加载一次的代码设置成随类加载,静态代码块可以有多个。

Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。

还有私有的方法不能被继承,子类就没有访问权限,肯定也是不能别覆盖的。

在这里插入图片描述

运行结果://~~Good night,Dick 这个例子说明“实例方法被覆盖,静态方法被隐藏”

4:是否可以在static环境中访问非static变量?

被static关键字修饰的变量或方法属于静态属性,它的内存是存储在JVM的方法区中。而实例中的变量是new(实例化)出来的,被存放到堆中。因为static静态属性(内存空间)是随着类的加载而加载。因为类是需要实例化出来才可以为了里面的变量分配内存(也就是说当类被加载的时候变量还不存在),所以静态方法不能使用非静态变量(还不存在)。

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

  • java支持的数据类型有两大类: 一类是基本数据类型分为8种 (数据) byte——(单字节) sort——(双字节) int——(4字节) long——(8字节) float——(4字节) double——(8字节) char——(双字节) boolean——(双字节) 前面四种为整数类型,5和6为浮点数类型,7为字符类型,8为布尔类型。

  • 一类是引用类型3种(除了数据还有地址编号) Object——(类类型) Array——(数组类型) Interface——(接口类型) JVM在解释java变量的时候是把它们按照数据类型区分的,基本数据类型的变量会存储在栈空间中,而引用类型在栈空间存放的是地址编号,它们的数据是存储在堆空间中的。

  • 查找引用类型变量的时候现在栈空间拿到该变量 在堆空间存储数据的地址编号,然后再到堆空间按照拿到的地址编号取数据。

  • 数据类型的自动装箱: 就是基本类型转换为相对应的引用类型。JVM重新安排它的存储空间,即把它的数据由原来的栈空间转移到堆空间中,并且生成一个堆空间的数据存储地址编号把它存储到栈空间中。

  • 数据类型的自动拆箱: 把通过自动装箱变成的引用类型转换成基本类型的过程,把它栈空间中地址编号拿出来并且按照地址编号取出堆中的数据。JVM重新分配存储空间,把数据存储到栈空间,销毁堆的地址编号(引用)。

6.Java中的方法覆盖【方法重写】(Overriding)和方法重载(Overload)是什么意思?

java中的方法重载发生在同一个类里面两个或者多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。 覆盖者可能不会限制它所覆盖的方法的访问。

  • 重载(Overloading)
  • (1)方法重载是让类以统一的方法处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数(类型)。重载Override是一个类中多态性的一种表现。
  • (2)java的方法重载,就是在类中可以创建多个方法,他们具有相同的名字,但具有不同参数和不同的定义。调用方法时通过传递给他们不同的参数个数和参数类型来决定具体使用那个方法,这就是多态性。
  • (3)重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不同。无法以返回类型来作为重载函数的区分标准。
  • 重写(Overriding)
  • (1)父类与子类的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写。在java中,子类可继承父类的方法,则不需要重新编写相同的方法。但有时子类并不想原封不动继承父类的方法,而是想做一定的修改,这就采用方法重写。方法重写又称方法覆盖。
  • (2)若子类中的方法与父类的中的某一方法具有相同的方法名、返回类型和参数表,则新方法覆盖原有的方法。如需要父类的原有方法,可以使用super关键字,该关键字引用房钱类的父类。
  • (3)子类函数访问权限大于父类。
  • 方法重写的原则:
  • 1.重写方法的方法名称、参数列表必须与原方法的相同,返回类型可以相同也可以是原类型的子类型(从Java SE5开始支持)。
  • 2.重写方法不能比原方法访问性差(即访问权限不允许缩小)。
  • 3.重写方法不能比原方法抛出更多的异常。
  • 4.被重写的方法不能是final类型,因为final修饰的方法是无法重写的。
  • 5.被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写。
  • 6.被重写的方法不能为static。如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足重写条件,那么会发生编译错误;反之亦然。即使父类和子类中的方法都是静态的,并且满足重写条件,但是仍然不会发生重写,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。
  • 7.重写是发生在运行时的,因为编译期编译器不知道并且没办法确定该去调用哪个方法,JVM会在代码运行的时候作出决定。
  • 方法重载的原则:
  • 1.方法名称必须相同。
  • 2.参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)。
  • 3.方法的返回类型可以相同也可以不相同。
  • 4.仅仅返回类型不同不足以成为方法的重载。
  • 5.重载是发生在编译时的,因为编译器可以根据参数的类型来选择使用哪个方法。
  • 重写和重载的不同:
  • 1.方法重写要求参数列表必须一致,而方法重载要求参数列表必须不一致。
  • 2.方法重写要求返回类型必须一致(或为其子类型),方法重载对此没有要求。
  • 3.方法重写只能用于子类重写父类的方法,方法重载用于同一个类中的所有方法。
  • 4.方法重写对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
  • 5.父类的一个方法只能被子类重写一次,而一个方法可以在所有的类中可以被重载多次。
  • 6.重载是编译时多态,重写是运行时多态。

7.Java中,什么是构造方法?什么是构造方法重载?什么是复制构造方法?

  • (1)新对象被创建的时候,会调用构造方法,每一个类都有构造方法。如果没有写,Java编译器会为这个类创建一个默认的构造方法。

  • (2)构造方法重载和方法重载很相似,可以为一个类创建多个构造方法。每一个构造方法必须有它自己唯一的参数列表。

  • (3)复制构造方法是c++中的,Java不支持,因为不自己写构造方法的情况下,java不会创建默认的复制构造方法。

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

    1.对象作为函数参数

    2.对象作为函数返回值

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

8.Java支持多继承么?

Java中类不支持多继承,只支持单继承(即一个类只有一个父类),但多继承的效果可以通过实现多个接口来间接完成(单继承,多实现)。 java中的接口支持多继承,即一个子接口可以有多个父接口。(接口的多继承不存在父接口中有同名方法的问题,因为接口中都是抽象方法,没有具体实现,不存在冲突,但是如果定义了相同的常量,则不能使用,会提示不明确的变量,必须通过接口名显示调用)

9.接口和抽象类的区别是什么?

一句概括:从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

  • ①接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
  • ②类可以实现很多个接口,但是只能继承一个抽象类。但是接口间可以多继承。
  • ③将类声明为抽象的,就可以不用实现接口或者抽象类的所有方法。
  • ④Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。 Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
  • ⑤接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是他们都可以new对象,即便两者中都没有方法,匿名内部类的使用。
  • ⑥如果抽象类中包含main方法的话是可以被调用的(通过类名的方式)。

10.什么是值传递和引用传递?

首先,不要纠结于 Pass By Value 和 Pass By Reference 的字面上的意义,否则很容易陷入所谓的“一切传引用其实本质上是传值”这种并不能解决问题无意义论战中。 更何况,要想知道Java到底是传值还是传引用,起码你要先知道传值和传引用的准确含义吧?可是如果你已经知道了这两个名字的准确含义,那么你自己就能判断Java到底是传值还是传引用。 这就好像用大学的名词来解释高中的题目,对于初学者根本没有任何意义。 一:搞清楚 基本类型 和 引用类型的不同之处 int num = 10; String str = "hello";

在这里插入图片描述
如图所示,num是基本类型,值就直接保存在变量中。而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容。 二:搞清楚赋值运算符(=)的作用 num = 20; str = "java";
在这里插入图片描述
对于基本类型 num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。

对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。

如上图所示,"hello" 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)

三:调用方法时发生了什么?参数传递基本上就是赋值操作。 第一个例子:基本类型 void foo(int value) { value = 100; } foo(num); // num 没有被改变

第二个例子:没有提供改变自身方法的引用类型 void foo(String text) { text = "windows"; } foo(str); // str 也没有被改变

第三个例子:提供了改变自身方法的引用类型 StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder.append("4"); } foo(sb); // sb 被改变了,变成了"iphone4"。

第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。 StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder = new StringBuilder("ipad"); } foo(sb); // sb 没有被改变,还是 "iphone"。

重点理解为什么,第三个例子和第四个例子结果不同? 下面是第三个例子的图解:

在这里插入图片描述
builder.append("4")之后
在这里插入图片描述
下面是第四个例子的图解:

在这里插入图片描述
builder = new StringBuilder("ipad"); 之后
在这里插入图片描述

说点其他:从局部变量/方法参数开始讲起:

局部变量和方法参数在jvm中的储存方法是相同的,都是在栈上开辟空间来储存的,随着进入方法开辟,退出方法回收。以32位JVM为例,boolean/byte/short/char/int/float以及引用都是分配4字节空间,long/double分配8字节空间。对于每个方法来说,最多占用多少空间是一定的,这在编译时就可以计算好。

我们都知道JVM内存模型中有,stack和heap的存在,但是更准确的说,是每个线程都分配一个独享的stack,所有线程共享一个heap。对于每个方法的局部变量来说,是绝对无法被其他方法,甚至其他线程的同一方法所访问到的,更遑论修改。

当我们在方法中声明一个 int i = 0,或者 Object obj = null 时,仅仅涉及stack,不影响到heap,当我们 new Object() 时,会在heap中开辟一段内存并初始化Object对象。当我们将这个对象赋予obj变量时,仅仅是stack中代表obj的那4个字节变更为这个对象的地址。

数组类型引用和对象: 当我们声明一个数组时,如int[] arr = new int[10],因为数组也是对象,arr实际上是引用,stack上仅仅占用4字节空间,new int[10]会在heap中开辟一个数组对象,然后arr指向它。

当我们声明一个二维数组时,如 int[][] arr2 = new int[2][4],arr2同样仅在stack中占用4个字节,会在内存中开辟一个长度为2的,类型为int[]的数组,然后arr2指向这个数组。这个数组内部有两个引用(大小为4字节),分别指向两个长度为4的类型为int的数组。

所以当我们传递一个数组引用给一个方法时,数组的元素是可以被改变的,但是无法让数组引用指向新的数组。 你还可以这样声明:int[][] arr3 = new int[3][],这时内存情况如下图

你还可以这样 arr3[0] = new int [5]; arr3[1] = arr2[0];

关于String: 原本回答中关于String的图解是简化过的,实际上String对象内部仅需要维护三个变量,char[] chars, int startIndex, int length。而chars在某些情况下是可以共用的。但是因为String被设计成为了不可变类型,所以你思考时把String对象简化考虑也是可以的。 String str = new String("hello")

当然某些JVM实现会把"hello"字面量生成的String对象放到常量池中,而常量池中的对象可以实际分配在heap中,有些实现也许会分配在方法区,当然这对我们理解影响不大。

总结:

作为一个新生程序猿,Damon希望能够与大家一同进步。文章或者描述有所不足的地方,希望大家多多提出来,一同进步。

Damon会继续发掘一些有用的咨询,知识以及新工具,与大家一同分享,谢谢!

过去文章都上传到github,有兴趣的小伙伴可以Star下:github.com/xxxyyh/Fron…