JavaSE基础面试总结

切勿背死知识
这些都是必须知道并且面试中常问到一些问题,我们在学习的时候不能死记硬背,我们更应该去融会贯通,在我们自己所做的项目中,哪里用到了这些知识,当面试官问到的时候,我们不能只说这些死知识,我们更应该说的是在自己的项目中的应用,这样你的回答才能够让面试官信服。

JavaSE

如何实现跨平台

对不同的平台有不同的虚拟机,虚拟机会将字节码文件解释为对应平台的指令.

​ JDK:java development kit 里面包含jdk和 编译器,能够创建和编译程序

​ JRE:java运行环境,为java程序提供运行环境,里面包括JVM,类库,java命令

​ JVM:java虚拟机,它是java实现跨平台的主要原因

谈谈你对面向对象的认识理解

面向对象是将一个事务模块化,分块完成,不像面向过程是流程化的,需要一步一步分析一步步实现,就拿一个大家熟知的事情,冰箱装大象,面向过程一步步来,打开门,大象进冰箱,关门. 面向对象则是分析其中的对象,有人有大象,有冰箱,然后每个对象有自己独特的功能和属性

面向对象的三大特征

封装
​ 隐藏类的信息,不向外界暴露,隐藏实现细节,向外提供特定的方法访问.

​ 就像挂在墙上的空调,我们并不知道他的内部的零件,但是可以通过遥控器来控制空调,

​ 成员变量私有化,单例模式是封装的一种体现

继承

​ 不同的对象之间可能会有共同点,就像我和同学之间,都有学生的特性,都有学号,有班级,每个学生也有自己独特的优势,继承在已存在的类上建立新类,就像孩子继承了父亲的 一些特征,他又有自己的新的属性,通过继承可以快速的创建有关联的新的类,提高代码的重用性

​ 子类拥有父类的非private的属性和方法

​ 可以对父类进行扩展(重写)
优点:

代码复用,易于扩展,维护

缺点:

打破了父类的封装性

父类和子类联系紧密,耦合度高 , 父类一旦发生了变化,子类可能会受影响

单一 聚合

面向对象设计7大原则

  1. 单一职责:类专注于一个功能,高内聚,低耦合
  2. 开闭:开放扩展(添加新功能,不改原由代码),关闭修改
  3. 里氏替换(多态):父类出现的地方,子类可以代替出现
  4. 依赖倒置:尽量依赖抽象,不依赖具体实现
  5. 接口隔离:为客户端提供小的单独接口
  6. 迪米特法则:实体之间应减少相互作用
  7. 聚合复用:尽量使用合成达到服用,减少继承,一个类中有另一个类的对象

多态(面试官最喜欢问的是你都在那些场景下使用了多态)
​ 一个对象的多种状态,封装和继承都是为多态服务的,方法的重载和重写是实现多态的两种方式
​ 三要素: 继承 重写 父类引用指向子类对象
​ 优点:提升程序可维护性,可扩展性
​ 缺点:不能调用子类特有的方法 解决方法:向下转型
好处
(1)向上转型:隐藏了子类类型,提高代码的扩展性。
(2)向下转型:可以使用子类特有功能。
弊端
(1)向上转型:只能使用父类共性的内容,无法使用子类特有功能。
(2)向下转型:容易发生类型转换异常(ClassCastException)

访问权限

public 修饰类,变量,方法 在任何地方都可以访问

proected 修饰变量,方法 在同包中,不通包的子类中访问

默认 修饰类,变量,方法 在同包类中可以访问

​ private 修饰变量,方法,内部类 只能在本类中访问

Java类初始化顺序

基类静态代码块,基类静态成员字段(并列优先级,按照代码中出现的先后顺序执行,且只有第一次加载时执行)——>派生类静态代码块,派生类静态成员字段(并列优先级,按照代码中出现的先后顺序执行,且只有第一次加载时执行)——>基类普通代码块,基类普通成员字段(并列优点级,按代码中出现先后顺序执行)——>基类构造函数——>派生类普通代码块,派生类普通成员字段(并列优点级,按代码中出现先后顺序执行)——>派生类构造函数.

java中创建对象的方式

  1. new + 类名
  2. 反射(Class类的newInstance方法、使用Constructor类的newInstance方法) Class.forName(“com.ff.Car”)
  3. 对象反序列化 IO(类实现Serializable接口,构建对象流ObjectOutputStream,用writeObject()将对象写入文件,构建ObjectInputStream读取文件,使用readObject() 构建一个对象)
  4. 对象clone()

对象创建过程(从JVM角度出发)

img

  1. 当遇到new时,去运行时常量池查找该引用指向的类有没有被加载
  2. 没有加载就进行类加载,解析和初始化
  3. 在堆内为该对象分配空间
  4. 分配完后,将对象的字段进行零值初始化,除去对象头,对创建的对象进行信息标记,所有的标记存放在对象头信息内,赋值为0或null
  5. 执行构造方法,调用对象的init方法进行初始化

对象头

实例数据:对象的实例数据就是在java代码中能看到的属性和他们的值

对齐填充字节:因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。

1. Mark Word

Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关,锁升级的过程和Mark Word有着密不可分的关系。

2. 指向类的指针

Java对象的类数据保存在方法区。

3.数组长度(只有数组对象才有)

包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。

对象头的另外一部分是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

对象克隆,浅克隆,深克隆

为什么使用克隆?

new的对象是一个全新的对象,里面没有数据,通过克隆的对象会保留被克隆对象的值

什么是对象克隆?

创建一个新的对象,将原对象中的数据复制到新对象中

浅克隆?实现cloneable

​ 基本类属于浅克隆,

​ 只将对象中关联的对象的引用地址复制属于浅克隆,不拷贝对象包含的引用指向的对象

深克隆?()实现serializable读取二进制流

​ 只将对象中关联的对象也进行了克隆,多级克隆.

解决多级克隆: 在关联的对象中 继续重写克隆方法

​ 通过对象序列化,反序列化.

构造方法

方法名与类名相同,没有返回值,不可以被void修饰

一个类中可以有多个构造方法, 默认有一个无参的构造(隐式),一旦显示的定义有参的构造方法,默认的就失效,

一般情况,显示的定义无参的构造方法

作用: 初始化创建对象的成员变量

new Car(int age,int a) Car() age=0; String name=null

重载和重写

​ 重载: 在一个类中,有多个名称相同的方法,参数不同,返回值和访问修饰符可以不同

​ 重写:在继承关系中,父类实现不能满足子类的需求,在子类中重写(覆盖,覆写)父类方法, 功能的扩展,super.父类()

​ 两同:方法名相同,参数相同

​ 一大:访问修饰符要大于等于父类

​ 一小:返回值小于等于父类,抛出的异常小于等于父类

为什么构造方法不能重写

​ 构造方法名与当前类名相同, 重写要求与父类方法结构一致.

​ 在子类的构造方法中调用父类的构造方法, 必须放在子类构造方法的第一行.

静态static

表示静态(指在内存只有一份的),被类的实例对象共享,可以任意赋值

修饰内部类,成员变量,成员方法,代码块,静态导包

都是随着类的加载而加载

非静态可以访问静态的

抽象类和接口

作用:抽象,功能定义 开闭原则,里氏替换原则都依赖接口,抽象类.

相同点:

​ 都可以包含抽象方法, 不能被实例化.

单继承多继承
可以有普通属性常量,被public static final修饰
抽象接口
有构造方法,不是创建对象,用来被调用进行初始化没有构造方法
可以有普通方法,抽象方法没有方法体default和static有方法体,抽象方法没有方法体

Object 类中的方法

   equals()    
   hashCode()   
   wait() 
   notify()   
   notifyAll() 
   finalize()   
   clone()

== 和 equals()的区别

Object()中的 equals()的源码
在这里插入图片描述

对于基本数据类型
== 和equals() 的作用是一样的,都是比较的是值是否一样


对于引用类型
==比较的是两个对象的地址是否相同,而equals()则需要看是否重写了Object类中的equals()方法,就比如下面的String类中,重写了equals()方法,它比较的就是对象中的值是否相同

在这里插入图片描述

String

特征

一旦创建时赋值后,值不可变.不可以被继承.
底层是用final修饰的char数组

final class String {final  char[]  value;
}

为什么设计为不可变

  1. 便于实现字符串常量池

​ 因为大量使用string,每次声明都会创建一个string对象,造成空间浪费,于是在堆里开辟了string pool,初始化string变量时现在里面找,有了就返回字符串的引用

  1. 使多线程安全

在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。

  1. 避免安全问题

在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。

  1. 加快字符串处理速度

由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。

总体来说,String不可变的原因要包括 设计考虑,效率优化,以及安全性这三大方面。

String的值不可以改变吗?

我们可以通过Java的反射机制进行改变

//通过反射改变String底层数组值
public static void main(String[] args) throws Exception {
        String s = "Hello World";
        System.out.println("s = " + s); //Hello World
        //获取String类中的value字段
        Field valueFieldOfString = String.class.getDeclaredField("value");
        //改变value属性的访问权限
        valueFieldOfString.setAccessible(true);
        //获取s对象上的value属性的值
        char[] value = (char[]) valueFieldOfString.get(s);
        //改变value所引用的数组中的第5个字符
        value[5] = '_';
        System.out.println("s = " + s);  //Hello_World
        System.out.println(s);//Hello_World
    }

String对象创建方式

String s1 = “abc”;​
方法1中,先在常量池中查找有没有"abc"这个字符串对象存在, ​
如果存在就把常量池中的指向这个字符串对象;


String s2 = new String(“abc”);
​ 方法2中,不论常量池中中是否已经存在"abc"这个字符串对象,都会新建一个对象。

        String strA  =   " abc " ;
        String strB  =   " abc " ;
        String strAA  =   new  String( " abc " );
        String strBB  =   new  String( " abc " );
        System.out.println(strA  ==  strB);//true
        System.out.println(strAA  ==  strBB);// false

创建了几个对象?

1.
String s = new String("abc")创建了几个对象
情况1: 创建两个对象
  String s = new String("abc");
  直接new对象,此前在字符串常量池中不存在"abc",此种情况在堆中创建一个对象,然后在字符串常量池中创建一个对象
情况2: 创建一个对象
  String s = "abc";
  String s1 = new String("abc");
  因为字符串常量池中已存在"abc",只需要在堆中创建即可.
2.    
创建了一个对象,底层优化创建了一个StringBuilder对象,append()追多个字符串对象,只产生一个对象.
 String st1 = "a" + "b" + "c";//只创建了一个字符串对象
 String st2 = "abc";
 System.out.println(st1 == st2);//true
3.
 String st1 = "ab";
 String st2 = "abc";
 String st3 = st1 + "c";//一旦发生拼接中有变量,会重新创建一个字符串对象
 System.out.println(st2 == st3);//false

String,StringBuffer,StringBuilder区别

String 值不可以改变 操作少量数据

StringBuffer 继承 AbstractStringBuilder类 值可变 线程安全的 多线程大量数据 16*2+2

StringBuilder 继承 AbstractStringBuilder类 值可变 线程不安全 单线程大量数据

基本类型和包装类

int和Integer区别

Integer是int的包装类,int则是java的一种基本数据类型

Integer变量必须实例化后才能使用,而int变量不需要

Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值

Integer的默认值是null,int的默认值是0

为每一个基本类提供了一个类包装基本类.使用面向对象方式操作

自动装箱( valueOf() ) Integer a = 10

注意 -128 +127 之间会从缓存池中直接获取

自动拆箱(intvalue() ) int b = a;

语法糖

所谓语法糖,可简单理解为Java平台为我们自动进行了一些转换,保证不同的写法在运行时等价。因此它们是发生在编译阶段的,也就是说生成的字节码是一致的。

自动装箱与拆箱实际上算是一种“语法糖”。

异常

什么是异常?

运行时异常,可以使用异常处理机制处理 不是错误(OOM)

异常体系

在这里插入图片描述

异常分类

运行时异常

编译期(检查)异常 编写代码时,需要处理

常见的异常类有哪些?

NullPointerException 空指针异常
ClassNotFoundException 指定类不存在
NumberFormatException 字符串转换为数字异常
IndexOutOfBoundsException 数组下标越界异常
ClassCastException 数据类型转换异常
FileNotFoundException 文件未找到异常
NoSuchMethodException 方法不存在异常
IOException IO 异常
SocketException Socket 异常

异常处理

try{
	//可能会发生异常代码 
  }catch( 异常类型  ){//处理异常
	return 0;
 }finally{//无论是否出现异常,都会执行
   return 1;
}//最终返回的是1,因为finally会覆盖catch中return的值

try{  
 	//可能出现异常的代码
}finally{
	//处理
}


//通过throws和throw来处理异常
  throws
​       用于在方法声明处, 表示此方法可能会出现某种异常(),然后在调用处处理

  throw
​       在方法体中,主动抛出一个异常对象.  当某种情况不满足时,向调用处抛出异常

自定义异常

​ 根据业务需要,自定义不同的异常类.

extends Exception{private int detail;public MyException(int a){this.detail  = a;}**toString();** //异常的打印信息

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值