Java基础常见面试题
1. Java语言的特点
1.简单易学,java语言继承了C++语言的优点,并去掉了C++中比较难的多继承、指针等概念。
2.面向对象,java也是一种面向对象的编程语言,它有三大特性,封装、继承、多态。
3.可移植性,java并不依赖平台,通过java虚拟机可以实现跨平台。
4.编译和解释并存,java程序由java虚拟机编译后生成虚拟机能够理解的代码,然后由解释器将虚拟机代码转换为特定系统的机器码执行。
5.健壮性,java语言的健壮性主要体现在强类型机制、异常处理、垃圾的自动收集等。
6.支持多线程,多线程机制使应用程序在同一时间并行执行多项任务
---以下是暂时不能理解的特性
7.安全性,java的存储分配模型是它防御恶意代码的主要方法之一。
8.分布性,java设计成支持在网络上应用,它是分布式语言。
9.高性能,java是一种先编译后解释的语言,所以它不如全编译性语言快,但设计者制作了“及时”编译程序,这样就可以实现全编译了。
10.动态性,java语言设计成适应于变化的环境,它是一个动态的语言。
2. Java和C++的区别
1.都是面向对象的语言,都支持封装、继承、多态。
2.java不提供指针来直接访问内存,程序内存更加安全。
3.java的类是单继承,C++支持多重继承;虽然java的类不可以多继承,但是接口可以多继承。
4.java有自动内存管理机制,无需程序员手动释放无用内存。
3. JVM和JRE、JDK之间的关系
JVM:其实就是java虚拟机,java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此java语言可以实现跨平台。
JRE:它包括JVM和java程序所需的核心类库等。核心类库主要是java.lang包,包含了运行java程序必不可少的系统类,
如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统会自动加载这个包。
【如果只想要运行一个开发好的java程序,计算机只需安装JRE即可。】
JDK:它包括了JRE和java的开发工具,如:编译工具(javac.exe),打包工具(jar.exe)等,它是提供给java的开发人员使用的。
具体关系如下图所示
4. 什么是Java语言的跨平台,原理是什么?
什么是java的跨平台:
指的是java语言编写的程序,一次编译之后,可以在多个不同系统平台上运行。
跨平台的原理:
java程序其实是通过java虚拟机在系统平台上运行的,只要该系统安装了相应的java虚拟机,该系统就能运行java程序。
5. 什么是字节码?采用字节码的好处
首先要知道java中的编译器和解释器的作用:
java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都能提供给编译程序
一个共同的接口。编译程序只需要面向虚拟机,由虚拟机的编译器编译后,生成虚拟机能够理解的代码,然后由解释器将虚拟机代码转换为特
定系统的机器码执行。
什么是字节码?
java源程序经过虚拟机编译器编译后产生的文件(即扩展名为.class的文件)。
采用字节码的好处?
java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以java程序
运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,java程序必须重新编译便可以在多种不同的计算机上运行。
6. Java的基础语法
6.1 java中注释有哪几种形式?
java中有三种注释形式:
//------------------------单行注释:通常用于解释方法内某单行代码的作用。
/* */---------------------多行注释:通常用于解释一段代码的作用。
/** */--------------------文档注释:通常用于生成java开发文档。
用得较多还是单行注释和文档注释,多行注释在实际的开发中使用相对较少。
代码的注释不是越详细越好。实际上好的代码本身就是注释,我们要尽量规范和美化自己的代码来减少不必要的注释。
若编程语言足够有表达力,就不需要注释,尽量通过代码来阐述。
举个例子:
去掉下面复杂的注释,只需要创建一个与注释所言同一事物的函数即可。
// check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
将其替换为
if (employee.isEligibleForFullBenefits())
6.2 标识符和关键字
标识符和关键字的区别
标识符:其实就是一个名字。编写程序时,需要给程序、类、变量、方法等取名字。
关键字:关键字就是被赋予特殊意义的标识符,只能用于特定的地方。
Java语言中关键字有哪些?
分类 | 关键字 | ||||||
访问控制 | private | protected | public | ||||
类、方法和变量修饰符 | abstract | class | extends | final | implements | interface | native |
new | static | strictfq | synchronized | transient | volatile | enum | |
程序控制 | break | continue | return | do | while | if | else |
for | instanceof | switch | case | default | assert | ||
错误处理 | try | catch | throw | throws | finally | ||
包相关 | import | package | – | – | – | – | – |
基本类型 | boolean | byte | char | double | float | int | long |
short | – | – | – | – | – | – | |
变量引用 | super | this | void | – | – | – | – |
保留字 | goto | const | – | – | – | – | – |
注意点:
1.所有的关键字都是小写的,在IDE中会以特殊颜色显示。
2.default这个关键字很特殊,即属于程序控制,也属于类、方法和变量修饰符,还属于访问控制。
3.在程序控制中,当switch中匹配不到任何情况时,可以使用defau来编写默认匹配的情况。
4.在类、方法和变量修饰符中,从JDK8开始引入默认方法,可以使用default关键字来定义一个方法的默认实现。
5.在访问控制中,如果一个方法前没有任何修饰符,则默认会有一个default,但是这个修饰符加上了就会报错。
6.虽然true,false和null看起来像关键字但实际上他们是字面值,同时你也不可以作为标识符来使用。
6.2.1 final关键字有什么用?
final可以用来修饰类、属性和方法:
- 被
final
修饰的类不可以被继承。 - 被
final
修饰的方法不可以被重写。 - 被
final
修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的。
6.2.2 final、finally、finalize的区别
-
final
可以修饰类、变量、方法、修饰类表示该类不能被继承,修饰方法表示该方法不能被重写,修饰变量表示该变量是一个常量不能被重新赋值。 -
finally
一般作用在try-catch代码块中,在处理异常的时候,我们将一定要执行的代码放到finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。 -
finalize
是一个方法,属于Object类的一个方法,而Object是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System.gc()方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可以回收的最后判断。
6.2.3 this关键字的用法
this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。this的用法在java中大体可以分为3种:
- 普通的直接引用,this相当于指向当前对象本身。
- 形参与成员变量名字重名时,可以用this来区分:
public Person(String name, int age) {
this.name = name;
this.age = age;
}
- 引用本类的构造器;this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name);
this.age = age;
}
}
6.2.4 super关键字的用法
super
可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。super
也有三种用法:
- 普通的直接引用,与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。
- 子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分。如下面代码所示
class Person{
protected String name;
public Person(String name) {
this.name = name;
}
}
class Student extends Person{
private String name;
public Student(String name, String name1) {
super(name);
this.name = name1;
}
public void getInfo(){
System.out.println(this.name); //Child
System.out.println(super.name); //Father
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student("Father","Child");
s1.getInfo();
}
}
- 引用父类构造器,super(参数):调用父类中的某一个构造器(应该为构造器中的第一条语句)。
6.2.5 this与super的区别
- this指的是当前对象,而super指的是当前对象的直接父类。
- 调用构造器的区别:this调用本类构造,务必放在构造方法的第一行。super调用父类构造,务必放在子类构造方法首行。
- 访问成员变量和方法的区别:this会先去访问本类中是否有此属性或方法,本类没有再去父类中查找,而super是直接访问父类中的属性和方法。
注意点:
1、this和super不能同时出现在一个构造函数里面。
因为this(){};这是调用本类无参构造时,如果系统发现在本类的无参构造的第一行没有this(Xxx xx) {} 有参构造调用 或者super调用,编译器会在无参构造中第一行自动加上super();这时就相当于两次调用super();也就是说父类进行了两次初始化。而实例化一个对象,一个构造器只能调用一次,所以会编译错误。如果在本类的无参构造中又调用了本类的有参构造,那么编译器会在本类的其他构造器的第一行加上super();也同样会编译错误。
2、为什么this调用本类构造,务必放在构造方法的第一行。super调用父类构造,务必放在子类构造方法首行。
首先,子类是从父类继承而来,继承了父类的属性和方法,如果在子类中不完成父类成员的初始化,则子类无法使用,因为在java中不允许调用没初始化的成员。在构造器中是顺序执行的,也就是说必须在第一行进行父类的初始化。而super能直接完成这个功能,因此,this()或者super()必须放在第一行。
其次,如上面第一点说的,this或者super不放在构造器的第一行,编译器也会在构造器中的第一行自动放一个空参的super构造器,其他的构造器也可以调用super或者this,也会造成编译错误。
6.2.6 static关键字
static的独特之处
- 被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象共享。
- 在类被第一次加载时,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。
- static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的。
- 被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。
static存在的意义 :
- 它能创建独立于具体对象的域变量或者方法。即使没有创建对象,也能使用属性调用方法!
- 形成静态代码块以优化程序性能。static代码可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static的顺序来执行每个static块,并且只会执行一次。为什么说static块可以可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化的操作都放在static代码块中进行。
static应用场景
- 修饰成员变量
- 修饰成员方法
- 静态代码块
- 修饰类【只能修饰内部类也就是静态内部类】
- 静态导包
static的注意事项
1、静态只能访问静态。
2、非静态既可以访问非静态的,也可以访问静态的。
6.3 java的运算符
6.3.1 自增运算符(++)和自减运算符(- -)
++和- - 运算符可以放在变量之前,也可以放在变量之后,当运算符放在变量之前时(前缀),先自增/减,再赋值;当运算符放在变量之后时(后缀),先赋值,再自增/减。例如:
b = ++a; 就是先自增(自己增加1),再赋值(赋值给b)
b = a++; 就是先赋值(赋值给b),再自增(自己增加1)
6.3.2 移位运算符
移位运算符时最基本的运算符之一,几乎每种编程语言都包含这一运算符。移位操作中,被操作的数据被视为二进制,移位就是将二进制数向左或者向右移动若干位的运算。
移位运算符在各种框架以及JDK自身的源码中使用还是挺广泛的,HashMap(JDK1.8)中的hash方法的源码就用到了位移运算符(hash方法源码如下)
static final int hash(Object key) {
int h;
// key.hashCode():返回散列值也就是hashcode
// ^ :按位异或
// >>>:无符号右移,忽略符号位,空位都以0补齐
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
在java代码中使用<<
、>>
和>>>
转换成的指令码运行起来会更高效些。
掌握最基本的移位运算知识还是很有必要的,不光可以帮助我们在代码中使用,还可以帮助我们理解源码中涉及到移位运算符的代码。
Java中有三种移位运算符:
-
<<
:左移运算符,向左移若干位,高位丢弃,低位补零。x<<1
,相当于x乘以2(不溢出的情况下)。 -
>>
:带符号右移,向右移若干位,高位补符号位,低位丢弃。正数高位补0,负数高位补1。x>>1
,相当于x除以2。 -
>>>
:无符号右移,忽略符号位,空位都以0补齐。
由于double
、float
在二进制中的表现比较特殊,因此不能来进行移位操作。
移位操作实际上支持的类型只有int
和long
,编译器在对short
、byte
、char
类型进行移位前,都会将其转换为int
类型再操作。
如果移位的位数超过数值所占有的位数会怎么样?
当int类型左移/右移位数大于等于32位操作时,会先求余(%)后再进行左移/右移操作。也就是说左移/右移32位相当于不进位操作(32%32=0),左移/右移42相当于左移/右移10位(42%32=10)。当long类型进行左移/右移操作时,由于long对应二进制是64位,因此求余操作的基数也变成了64。
也就是说:x位int类型时,x<<42
等同于x<<10
, x>>42
等同于 x>>42
,x>>>42
等同于x>>>10
。
左移运算符代码示例:
int i = -1;
System.out.println("初始数据: " + i);
System.out.println("初始数据对应的二进制字符串: " + Integer.toBinaryString(i));
i <<= 10;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
输出结果:
初始数据: -1
初始数据对应的二进制字符串: 11111111111111111111111111111111
左移 10 位后的数据 -1024
左移 10 位后的数据对应的二进制字符 11111111111111111111110000000000
由于左移位数大于等于32位操作时,会先求余(%)后再进行左移操作,所以下面的代码左移42位相当于左移10位(42%32=10),输出结果和前面的代码是一样的。
int i = -1;
System.out.println("初始数据: " + i);
System.out.println("初始数据对应的二进制字符串: " + Integer.toBinaryString(i));
i <<= 42;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
右移运算符使用类似。
&、| 、^、~(按位与、按位或、按位异或、按位取反)
- &(按位与):参加运算的两个数,按二进制位进行与运算。
运算规则:两个数的二进制同为1,结果才为1,否则为0。 - |(按位或):参加运算的两个数,按二进制位进行或运算
运算规则:两个数的二进制只要有一个为1结果就是1。 - ^(异或运算符):参加运算的两个数,按二进制位进行异或运算
运算规则:两个数的二进制值不同,结果为1。 - ~(按位取反):将内存中的补码按位取反(包括符号位),所以先要得到补码。
1.对正数进行按位取反:原码 = 反码 = 补码 --> 按位取反 --> 减一变成原码 --> 符号位不变再取反得到反码
2.对负数进行按位取反:原码 --> 保留符号位取反 --> 反码 -->加1 -->补码 -->按位取反包括符号位 --> 正数补码=原码=反码
具体示例:
~1(正数的按位取反运算)
1的原码 0000 0001
1的反码 0000 0001
1的补码 0000 0001
按位取反操作 1111 1110
#负数(因为最高位为1,表示为负数),就需要将其变为原码,补码变为原码,首先先减1
变为原码先减一 1111 1101
#变为原码时,符号位不变
再取反 1000 0010 (-2)
~-1(负数的按位取反运行)
-1的原码 1000 0001
-1的反码 1111 1110
-1的补码 1111 1111
按位取反操作 0000 0000
#因为正数的补码、反码、原码都是一个样。
变为原码 0000 0000 (0)
&与&&的区别
&运算符有两种用法:按位与 和 逻辑与
&&运算符是短路与运算。逻辑与跟短路与的差别非常大,虽然两者都要求运算符左右两端的布尔值都是true,整个表达式的值才是true。&&之所以称为短路与运算,是因为如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。
注意: 逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
6.4 continue、break和return的区别是什么?
在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。但是有时候可以需要在循环的过程中,当发生某种条件后,提前终止循环,这就需要用到下面几个关键词。
-
continue
:指跳出当前的这一次循环,继续下一次循环。 -
break
:指跳出整个循环体,继续执行循环下面的语句。
return
是用于跳出所在方法,结束该方法的运行。return一般有两种用法:
-
return;
:直接使用return结束方法执行,用于没有返回值函数的方法。 -
return value;
:return一个特定值,用于有返回值函数的方法。
下面这段程序的运行结果是?
public static void main(String[] args) {
boolean flag = false;
for (int i = 0; i <= 3; i++) {
if (i == 0) {
System.out.println("0");
} else if (i == 1) {
System.out.println("1");
continue;
} else if (i == 2) {
System.out.println("2");
flag = true;
} else if (i == 3) {
System.out.println("3");
break;
} else if (i == 4) {
System.out.println("4");
}
System.out.println("xixi");
}
if (flag) {
System.out.println("haha");
return;
}
System.out.println("heihei");
}
运行结果:
0
xixi
1
2
xixi
3
haha
6.5 变量
成员变量和局部变量的区别?
- 语法形式: 从语法形式上看,成员变量属于类的,而局部变量是在代码块或者方法中定义的变量或者是方法的参数;成员变量可以被
public
,private
,static
等修饰符所修饰,而局部变量不能被访问控制修饰符及static
所修饰;但是,成员变量和局部变量都能被final
所修饰。 - 存储方式: 从变量在内存中的存储方式来看,如果成员变量是使用
static
修饰的,那么这个成员变量是属于类的,如果没有使用static
修饰,这个成员变量是属于实例的。成员变量随着对象的创建而存在,随着对象的消失而消失,而对象存在于堆内存,局部变量是在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。 - 生存时间: 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
- 默认值: 从变量是否有默认值来看,成员变量如果没有被赋予初始值,则会自动以类型的默认值而赋值(一般情况例外:被
final
修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。所以局部变量使用前必须赋值。
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
Single backticks |
| ‘Isn’t this fun?’ |
Quotes |
| “Isn’t this fun?” |
Dashes |
| – is en-dash, — is em-dash |
创建一个自定义列表
HTML conversion tool
Authors
John
Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五
这将产生一个流程图。:
链接
长方形
圆
圆角长方形
菱形
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
- mermaid语法说明 ↩︎
- 注脚的解释 ↩︎