万丈高楼平地起之Java基础一

264 阅读17分钟

  今天去面试一家公司,根据简历上Java基础知识扎实,把我给问住;最后面试官强烈要求我把《Java核心技术》上下两册完完整整的看完,我脸红了!很是羞愧,认真反思自己的Java基础真的不牢靠,应验了那一句话:基础不牢,地动山摇。等我看完这两册书,会在文中提及到,收获与感悟。

1. 面向对象和面向过程的区别

  • 面向过程 :面向过程就是清楚知道每一个步骤应该怎么走,由已经写好的逻辑来控制,不会考虑其他逻辑没有的东西,要么有结果,要么没结果。就性能而言面向过程性能比面向对象高, 因为类调用时需要实例化,开销比较大,比较消耗资源。缺点就是面向过程没有面向对象易维护、易复用、易扩展。
  • 面向对象 :面向对象只关心结果,具体怎么实现不去管他,面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。相比较面向过程,自然性能就会低一点。

2. Java 语言基本数据类型?

  1. 整数类型:A:byte型,占一个字节;B:short型,占两个字节;C:int型,占四个字节;D:long型,占八个字节。
  2. 浮点类型:A:float 型,占四个字节;B:double型:占八个字节。
  3. 字符类型:char型,单个字符,占两个字节,例如 "A"表示65(这里将引出编码格式,在日后将会说明)。
  4. 布尔类型:Boolean型 ,值为false true,常用于判断条件。

3. 字符型常量和字符串常量的区别?

  1. 形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符。
  2. 含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。
  3. 占内存大小 字符常量只占2个字节; 字符串常量占若干个字节(至少一个字符结束标志) 。


4.  JVM 、JDK 和 JRE 通俗的解释

JVM

Java虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM有针对不同系统(Windows,Linux,macOS)的特定实现,目的是运行相同的字节码,都会给出相同的结果。

什么是字节码?采用字节码的好处是什么?

在 Java 中,JVM可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。而且,由于字节码并不针对一种特定的机器,因此,Java程序无须重新编译便可在多种不同操作系统的计算机上运行。

JDK 和 JRE

JDK(Java Development Kit)它拥有JRE所拥有的一切,还有编译器(javac)和工具(如javadoc和jdb)。

JRE (Java Runtime Environment)是 Java运行时环境,是运行已编译 Java 程序所需的所有内容的集合,包括 Java虚拟机(JVM),Java类库,java命令和其他的一些基础构件。

5. JDK1.7 和 JDK1.8 的对比(Java8的新特性将会在后面详细介绍)

# JDK 7,代号Dolphin(海豚) 2011-07-28发布,这是sun被oracle收购(2009年4月)后的第一个版本,主要更新内容: 

1.switch语句块中允许以字符串作为分支条件 
2.在创建泛型对象时应用类型推断,比如你之前版本使用泛型类型时这样写 ArrayList<User> userList= new ArrayList<User>();,这个版本只需要这样写 ArrayList<User> userList= new ArrayList<>();,也即是后面一个尖括号内的类型,JVM 帮我们自动类型判断补全了。 
3.在一个语句块中捕获多种异常 
4.添加try-with-resources语法支持,使用文件操作后不用再显示执行close了。
5.支持动态语言 
6.JSR203, NIO.2,AIO,新I/O文件系统,增加多重文件的支持、文件原始数据和符号链接,支持ZIP文件操作 
7.JDBC规范版本升级为JDBC4.1
8.引入Fork/Join框架,用于并行执行任务 
9.支持带下划线的数值,如 int a = 100000000;,0 太多不便于人阅读,这个版本支持这样写 int a = 100_000_000,这样就对数值一目了然了。  
10.Swing组件增强(JLayer,Nimbus Look Feel…

# JDK 8 2014-3-19发布,oracle原计划2013年发布,由于安全性问题两次跳票,是自JAVA5以来最具革命性的版本,主要更新内容: 

1.接口改进,接口可以定义默认方法实现和静态方法了。
2.引入函数式接口
3.引入Lambda表达式
4.引入全新的Stream API,提供了对值流进行函数式操作。
5.引入新的Date-Time API
6.引入新的JavaScrpit引擎Nashorn
7.引入Base64类库
8.引入并发数组(parallel)
9.添加新的Java工具:jjs、jdeps
10.JavaFX,一种用在桌面开发领域的技术
11.静态链接 JNI 程序库


6. Java和C++的区别?

我知道很多人没学过 C++,但是面试官就是没事喜欢拿咱们 Java 和 C++ 比呀!没办法!!!就算没学过C++,也要记下来!

  • 都是面向对象的语言,都支持封装、继承和多态。
  • Java 不提供指针来直接访问内存,程序内存更加安全。
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存。

7. Java 面向对象编程三大特性: 封装 继承 多态

封装

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。例如:person 对象 ,属性是:name、age、sex等,提供set/getName、set/getAge方法来设置/获取person这个对象的相对应的数值。

继承

继承是使用已存在的类的定义作为基础建立新类的技术,继承是指子类要拥有父类全部的功能,不可以选择性继承父类,可以增加新的数据或新的功能。

多态

多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

关于继承如下三点请留意:

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
  2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  3. 子类可以用自己的方式实现父类的方法。(以后介绍)。

8. 重载和重写的差别

  • 重载(overload): 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。   
  • 重写(override): 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。

9. 构造器是否可被重写?

在讲继承的时候我们就知道父类的私有属性和构造方法并不能被继承,所以 Constructor 也就不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

10. 加载顺序

一、类的加载过程 (类加载过程以及类加载器将会在后面详细介绍)

首先,Jvm在执行时,遇到一个新的类时,会到内存中的方法区去找class的信息,如果找到就直接拿来用,如果没有找到,就会去将类文件加载到方法区。在类加载时,静态成员变量加载到方法区的静态区域,非静态成员变量加载到方法区的非静态区域。 静态代码块是在类加载时自动执行的代码,非静态代码块是在创建对象时自动执行的代码,不创建对象不执行该类的非静态代码块。 

 加载过程:

 1、JVM会先去方法区中找有没有相应类的.class存在。如果有,就直接使用;如果没有,则把相关类的.clss加载到方法区。  

2、在.class加载到方法区时,先加载父类再加载子类;先加载静态内容,再加载非静态内容。 

3、加载静态内容: 把.class中的所有静态内容加载到方法区下的静态区域内 静态内容加载完成之后,对所有的静态变量进行默认初始化 所有的静态变量默认初始化完成之后,再进行显式初始化 当静态区域下的所有静态变量显式初始化完后,执行静态代码块 。

4、加载非静态内容:把.class中的所有非静态变量及非静态代码块加载到方法区下的非静态区域内。 

 5、执行完之后,整个类的加载就完成了。 对于静态方法和非静态方法都是被动调用,即系统不会自动调用执行,所以用户没有调用时都不执行,主要区别在于静态方法可以直接用类名直接调用(实例化对象也可以),而非静态方法只能先实例化对象后才能调用。

 二、对象的创建过程 

1、new一个对象时,在堆内存中开辟一块空间。 

 2、给开辟的空间分配一个地址。 

 3、把对象的所有非静态成员加载到所开辟的空间下。

 4、所有的非静态成员加载完成之后,对所有非静态成员变量进行默认初始化。  

5、所有非静态成员变量默认初始化完成之后,调用构造函数。  

6、在构造函数入栈执行时,分为两部分:先执行构造函数中的隐式三步,再执行构造函数中书写的代码。 

隐式三步:
①执行super()语句
 
②显示初始化(对开辟空间下的所有非静态成员变量进行)

③执行构造代码块 

7、在整个构造函数执行完并弹栈后,把空间分配的地址赋给引用对象。

三、类加载顺序(包含子类) 

(1) 父类静态代码块(包括静态初始化块,静态属性,按照出现顺序) 

(2) 子类静态代码块(包括静态初始化块,静态属性,按照出现顺序 ) 

(3) 父类非静态代码块( 包括非静态初始化块(比非静态属性稍晚),非静态属性 ) 

(4) 父类构造函数 

(5) 子类非静态代码块 ( 包括非静态初始化块(比非静态属性稍晚),非静态属性 ) 

(6) 子类构造函数 其中:类中静态块按照声明顺序执行,并且(1)和(2)不需要调用new类实例的时候就执行了(意思就是在类加载到方法区的时候执行的) 其次,需要理解子类覆盖父类方法的问题,也就是方法重写实现多态问题。 

11. 在一个静态方法内调用一个非静态成员为什么是非法的?

由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。假如静态方法需要的非静态变量是还没有新建,那么执行程序会出现异常。

12. String StringBuffer 和 StringBuilder 的差别是什么? String 为什么是不可变的?

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的。

线程安全性

String 中的对象是不可变的,也就可以理解为常量,线程安全。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。 

性能

每次对 String 类型的值进行改变的时候,都会生成一个新的 String 对象,然后取得更后的内存地址。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结:

  1. 操作少量的数据: 适用String
  2. 单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer

13. 基本类型的包装类(基本类型原子类将在后文JUC包原子类总结详细介绍)

 Java是一种纯面向对象语言,但是java中有8种基本数据类型,破坏了java为纯面向对象的特征。为了承诺在java中一切皆对象,java又给每种基本数据类型分别匹配了一个类,这个类我们称之为包装类。

byte =========Byte;
short=========Short;
int===========Integer;
long==========Long;
float==========Float;
double========Double;
boolean========Boolean;
char===========Character;

14. 自动装箱与拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

15. 无参构造函数的意义

  Java 程序在执行子类的构造方法之前,会先调用父类中的无参构造方法。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,且没有找到父类的无参构造方法用于初始化,则编译时将发生错误。

16. 类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么?

主要作用是完成对类对象的初始化工作。可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法(前提是自己没有给出无参构造方法,建议是每次都是自己给出,确保一定有无参构造方法)。

17. 构造方法有哪些特性?

  1. 名字与类名相同。
  2. 没有返回值,但不能用void声明构造函数。
  3. 生成类的对象时自动执行,无需调用。

18. 静态方法和实例方法有何不同

  1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

  2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。

19. 接口和抽象类的差别是什么?

  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. 接口中除了static、final变量,不能有其他变量,而抽象类中则不一定。
  3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展多个接口。
  4. 抽象方法可以有public、protected和default这些修饰符(抽象方法就是为了被重写所以不能使用private关键字修饰!)。
  5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。

注意:在JDK8中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。

20. 成员变量与局部变量的区别有哪些?

  1. 从语法形式上看:成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
  2. 从变量在内存中的存储方式来看:如果成员变量是使用static修饰的,那么这个成员变量是属于类的,如果没有使用static修饰,这个成员变量是属于实例的。成员变量存在于堆内存,局部变量则存在于栈内存。
  3. 从变量在内存中的生存时间上看:成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
  4. 成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。

一家之言,如果有什么错误还望大家指出,本文很多知识点引用:github.com/Snailclimb/…