面试题汇总一 Java 语言基础篇

前言

题目汇总来源 史上最全各类面试题汇总,没有之一,不接受反驳

春招的进度有些太快了,不知道现在开始学还来不来得及。跟着上面那篇公众号文章里的问题学的,顺便做个记录。

 


目录

前言

Java基础知识

面向对象三大特征

面向对象六大原则

Java可见性修饰符

Java设计模式

Java中==和equals和hashCode的区别 

Object类的equal和hashCode方法重写,为什么?

Java基本类型及其占用空间

int与integer的区别

类变量初始化顺序

抽象类的意义

接口和抽象类的区别

能否创建一个包含可变对象的不可变对象?

谈谈对java多态的理解

Java中String的了解

String、StringBuffer、StringBuilder区别

你对String对象的intern()熟悉么?

String为什么要设计成不可变的?

泛型中extends和super的区别

进程和线程的区别

final,finally,finalize的区别

序列化的方式

如何将一个Java对象序列化到文件里?

String 转换成 integer的方式及原理

静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

讲一下常见编码方式?

如何格式化日期?

Java的异常体系

什么是异常链

throw和throws的区别

说说你对Java反射的理解

反射的原理,反射创建类实例的三种方式是什么

反射中ClassLoader.loadClass和class.ForName区别

java当中的四种引用

深拷贝和浅拷贝的区别是什么?

什么是编译期常量?使用它有什么风险?

a=a+b与a+=b有什么区别吗?

静态代理和动态代理的区别,什么场景使用?

Java中实现多态的机制是什么?

说说你对Java注解的理解

说说你对依赖注入的理解

说一下泛型原理,并举例说明


 

Java基础知识

面向对象三大特征

面象对象的三大特征

  • 继承:子类继承父类的特征和行为
  • 封装:隐藏内部实现,暴露公共行为
  • 多态:不同对象对一个动作有不同表现

 

面向对象六大原则

设计模式:面向对象设计的六大原则 (绝对详细)

  • 单一职责原则 SRP:一个类只担负一个职责
  • 开闭原则 OCP:类对扩展开放,对修改关闭
  • 里式替换原则 LSP:引用基类的地方必须能透明地使用其子类对象
  • 依赖倒置原则 DIP:依赖抽象接口,不依赖具体实现
  • 接口隔离原则 ISP:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
  • 迪米特原则 LOD:低耦合,高内聚

 

Java可见性修饰符

Java 修饰符

public > protected > default > private

 

Java设计模式

设计模式 | 菜鸟教程

Java之美[从菜鸟到高手演变]之设计模式

Java之美[从菜鸟到高手演变]之设计模式二

Java之美[从菜鸟到高手演变]之设计模式三

Java之美[从菜鸟到高手演变]之设计模式四

 

Java中==和equals和hashCode的区别 

java中equals,hashcode和==的区别

1. 基本数据类型用==进行比较。

2. 引用使用==判断是否指向同一个对象,也可覆盖equals方法实现自定义判断。

3. hashCode生成hash码。同一对象一定生成相同的hash码

4. hashCode和equals的关系

  • 同一对象上多次调用hashCode()方法,总是返回相同的整型值。

  • 如果a.equals(b),则一定有a.hashCode() 一定等于 b.hashCode()。 

  • 如果!a.equals(b),则a.hashCode() 不一定不等于 b.hashCode()。此时如果a.hashCode() 总是不等于 b.hashCode(),会提高hashtables的性能。

  • a.hashCode() == b.hashCode() 则 a.equals(b)可真可假

  • a.hashCode() != b.hashCode() 则 a.equals(b)为假。 

 

Object类的equal和hashCode方法重写,为什么?

  1. 如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 
  2. 如果两个对象不equals,他们的hashcode有可能相等。 
  3. 如果两个对象hashcode相等,他们不一定equals。 
  4. 如果两个对象hashcode不相等,他们一定不equals。 
  • 若重写 equals(Object obj) 方法,有必要重写 hashcode() 方法,确保通过 equals(Object obj) 方法判断结果为true的两个对象具备相等的 hashcode() 返回值。说得简单点就是:“如果两个对象相同,那么他们的 hashcode 应该相等”。不过请注意:这个只是规范,如果你非要写一个类让 equals(Object obj) 返回 true 而 hashcode() 返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。 
  •  一个很常见的错误根源在于没有覆盖 hashCode 方法。在每个覆盖了 equals 方法的类中,也必须覆盖 hashCode 方法。如果不这样做的话,就会违反 Object.hashCode 的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括 HashMap、HashSet 和 Hashtable。

 

Java基本类型及其占用空间

byte 1字节
short 2字节
char 2字节
int 4字节
float 4字节
boolean 4字节
long 8字节
double 8字节

 

int与integer的区别

java面试题之int和Integer的区别

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

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

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

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

 

类变量初始化顺序

  1. 父类显式静态初始化代码块
  2. 子类显式静态初始化代码块
  3. 父类非静态实例初始化代码块
  4. 父类构造函数
  5. 子类非静态实例初始化代码块
  6. 子类构造函数

 

抽象类的意义

一个类中如果包含抽象方法,这个类应该用abstract关键字声明为抽象类。

意义:

1. 为子类提供一个公共的类型;

2. 封装子类中重复内容(成员变量和方法);

3. 定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的。

 

接口和抽象类的区别

深入理解Java的接口和抽象类

抽象类:

包含抽象方法的类要用abstract修饰(虽然没有抽象方法也可以)。

和普通类一样可以有普通成员变量和方法。

接口:

接口中所有的方法不能有具体的实现。

变量会被隐式地指定为public static final变量

 

能否创建一个包含可变对象的不可变对象?

原公众号放答案了,自己看吧?

Java面试题(二):你真的懂这几道题了吗?

 

谈谈对java多态的理解

java提高篇(四)-----理解java的三大特性之多态

JAVA重写和重载的区别

概念:

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。

父类引用指向子类对象,可以提高代码的灵活性和可扩展性。

实现条件:

继承、重写、向上转型。

实现形式:

继承和接口。

重载规则:必须具有不同的参数列表; 可以有不同的返回类型;可以有不同的访问修饰符;可以抛出不同的异常。

重写规则:参数列表必须完全相同;返回类型必须相同;访问修饰符的限制一定要大于等于被重写方法的访问修饰符;重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。

 

Java中String的了解

emmm有点泛,随便贴个链接

深入理解Java中的String

 

String、StringBuffer、StringBuilder区别

图析:String,StringBuffer与StringBuilder的区别

探秘Java中的String、StringBuilder以及StringBuffer

String:不可变,每次修改都会生成新的类

StringBuffer:可变,线程安全

StringBuilder:可变,线程不安全,速度快

 

你对String对象的intern()熟悉么?

String的Intern方法详解

intern() 对 String s = new String(...) 这样的形式有效,要保证此时字符串在堆中而不在常量池中。

intern() 会先判断字符串是否在常量池中,如果不是,则将字符串放入常量池中;如果在常量池中,则直接返回自身。

 

String为什么要设计成不可变的?

java中String类设计成不可变的原因

总结就是两点:安全和性能

安全:不可变对象线程安全;作为最常用的数据类型设置为不可变可保证数据安全。

性能:不变性可引入常量池概念,复用字符串,节省空间,提高性能。

 

泛型中extends和super的区别

Java 泛型 <? super T> 中 super 怎么 理解?与 extends 有何不同?

<? extends T>:

上界通配符(Upper Bounds Wildcards)

只能输出T及其父类,也就是返回值为T及其父类(只取不存)

<? super T>:

下界界通配符(Lower Bounds Wildcards)

只能输入T及其子类,也就是传入参数为T及其子类(只存不取)

<?>:

既不能用于入参也不能用于返参

PECS原则:

Producer Extends Consumer Super

频繁往外读取内容的,适合用上界Extends。

经常往里插入的,适合用下界Super。

 

进程和线程的区别

进程和线程的区别

进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

线程:是进程的一个执行单元,是进程内的调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

一个程序至少一个进程,一个进程至少一个线程。

 

final,finally,finalize的区别

final、finally与finalize的区别

final:修饰符,可修饰类、方法、成员变量

finally:用于try/catch语句后,若try语句被执行则finally一定会执行

finalize:在java.lang.Object里定义的,gc时调用,且只调用一次

 

序列化的方式

如何将一个Java对象序列化到文件里?

Java中实现序列化的两种方式 Serializable 接口和 Externalizable接口

Serializable:

一个对象的所有属性包括私有属性都可以序列化,可以使用 transient 关键词排除序列化的属性。

serialVersionUID 保证对象的一致性。

使用 ObjectInputStream 和 ObjectOutputStream 实现对象的序列化和反序列化。

Externalizable:

Serializable 接口的子类。

writeExternal() 和 readExternal() 方法可以指定序列化哪些属性。

更多序列化方式:几种Java序列化方式的实现

 

String 转换成 integer的方式及原理

string转换成integer的方式及原理

Integer.parseInt(String str)方法,具体思路其实就是一个字符一个字符的转换。

要注意的就是边界值的处理。

 

静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

Java静态属性与静态方法能否被继承的问题

能被继承,不能被重写。

谁调用静态函数,执行的静态函数就属于谁。

 

成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

Java内部类详解--成员内部类,局部内部类,匿名内部类,静态内部类

成员内部类:

定义于一个类内部,依附于外部类存在。

可访问外部类所有成员变量和成员方法,包括 private 和 static。

外部类访问内部类需通过内部类对象的引用。

修饰符 private, protected, public。

局部内部类:

定义于方法或作用域内。

不能有修饰符。

只能访问 final 局部变量。

匿名内部类:

在实现父类或接口时生成对象,没有构造器。

不能有修饰符。

只能访问 final 局部变量。

静态内部类:

定义于外部类,有static关键字,不依附于外部类。

未持有外部类引用,不能使用外部类的非static成员变量或者方法。

 

讲一下常见编码方式?

Java几种常见的编码方式

常见的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等

 

如何格式化日期?

Java date format 时间格式化

通过 java.text.DateFormat 格式化日期。

// 2019年2月23日 星期六
String s1 = DateFormat.getDateInstance(DateFormat.FULL).format(new Date());
// 2019-02-23 10:59:29
String s2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());

 

Java的异常体系

Java异常体系结构

你现在就必须知道的Java异常体系

异常类型:

Throwable 可分为 Error 和 Exception。

Exception 可分为 RuntimeException(不受检异常) 和其他异常(受检异常)。

异常捕获:

try-catch-finally体系。

异常抛出:

throw 和 throws 关键词。

异常转译与异常链

 

什么是异常链

关于JAVA异常链的学习

捕获一个异常后抛出另外一个异常,并且把异常原始信息保存下来,这被称为异常链。

Throwable及其子类的构造器可以接收一个cause参数追踪到异常最初发生的位置。

 

throw和throws的区别

throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常, 则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。 如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。 如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。

throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常。仅当抛出了检查异常, 该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出, 而不是囫囵吞枣一般在catch块中打印一下堆栈信息做个勉强处理。

 

说说你对Java反射的理解

反射的原理,反射创建类实例的三种方式是什么

java中的反射

【类反射】类反射原理和获取Class对象的三种方式

反射:

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。

Class类:

描述类的类,封装了描述方法的 Method描述字段的 Filed,,描述构造器的 Constructor 等属性。

Java高级特性——反射

Class类加载的三种方式:

try {
    Class c1 = new Food().getClass();
    Class c2 = Food.class;
    Class c3 = Class.forName("GenericTest.Food");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

 

反射中ClassLoader.loadClass和class.ForName区别

forName()会初始化类,将.class文件加载到jvm中,还会执行类中的static块,还会执行给静态变量赋值的静态方法。

loadClass()只将class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

 

java当中的四种引用

详解 Java 中的四种引用

强引用 - FinalReference:

最常见的引用。

String str1 = new String("str");

软引用 - SoftReference:

在快报 OutOfMemoryError 异常时GC回收。

SoftReference<String> str2 = new SoftReference<>(new String("str"));

弱引用 - WeakReference:

只能活到下一次GC前,一旦发生GC就会被回收。

WeakReference<String> str3 = new WeakReference<>(new String("str"));

虚引用 - PhantomReference:

完全不影响对象的生命周期,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知

必须配合引用队列使用。

ReferenceQueue<String> rq = new ReferenceQueue<>();
PhantomReference<String> str4 = new PhantomReference<>(new String("str"), rq);

引用队列 - ReferenceQueue:

软引用(SoftReference)和引用队列(ReferenceQueue)

软引用、弱引用和虚引用通过 get() 方法获得对象的强引用,若对象已被回收,则返回 null。

若为这三种引用指定引用队列,则在对象回收后,会将空的引用放入引用队列中以供后续处理。

 

深拷贝和浅拷贝的区别是什么?

【Java深入】深拷贝与浅拷贝详解

对象要克隆需要实现 Cloneable 接口。

浅拷贝只复制了对象本身,使得对象内部的引用可能指向同一个对象;

深拷贝将拷贝对象内部引用的对象也都复制一遍。

 

什么是编译期常量?使用它有什么风险?

Java编译期常量解释及其使用风险

在编译时就能确定这个常量的具体值,与运行时常量对应。

由于编译期常量在编译时就确定了值,编译时会直接替换成对应的值。

对应到实际业务中,可能是我们的程序中使用了一个第三方库中公有的编译期常量时,如果对方更新了该常量的值,而我们随后也只更新依赖的jar包,那么我们的程序中该常量就是老值,就会产生隐患。为了避免这种情况,在更新依赖的jar文件时,应该重新编译我们的程序。

 

a=a+b与a+=b有什么区别吗?

[短文速读] a=a+b和a+=b的区别

若 a 的范围比 b 小,则 a += b 有自动的类型转换,而 a =  a + b 没有类型转换,会报错,需要强制转换。除此之外基本相同。

 

静态代理和动态代理的区别,什么场景使用?

Java静态代理与动态代理 理解与应用场景

动态代理与静态代理区别

概念:

为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。 

角色:

接口

委托类:真正执行任务的类

代理类:代理类持有一个委托类的对象引用

静态代理:

编译期就确定代理关系

动态代理:

在代码运行期间加载被代理的类

Proxy.newProxyInstance(接口类的ClassLoader, 接口类的class, 实现了InvocationHandler的类)

// 接口 java.lang.Runnable

// 委托类
class Peer implements Runnable {
    @Override
    public void run() {
        System.out.println("This is peer!");
    }
}

// 代理类
class Ih implements InvocationHandler {
    Object o;

    public Object bind(Object o) {
        this.o = o;
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法执行前操作
        System.out.println("before");

        Object result = method.invoke(o, args);

        // 方法执行后
        System.out.println("after");

        return result;
    }
}

// 运行
Runnable p = (Runnable) new Ih().bind(new Peer());
p.run();

// 输出
before 
This is peer! 
after 

 

Java中实现多态的机制是什么?

方法的重写 Overriding 和重载 Overloading 是Java多态性的不同表现。

重写 Overriding 是父类与子类之间多态性的一种表现。

重载 Overloading 是一个类中多态性的一种表现。

 

说说你对Java注解的理解

Java注解基本原理

什么是注解(Annotation):

Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。

Java SE5内置标准注解:

@Override,表示当前的方法定义将覆盖超类中的方法。

@Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。

@SuppressWarnings,关闭不当编译器警告信息。

用于创建注解的注解:

@Target

表示该注解可以用于什么地方,可能的ElementType参数有:

CONSTRUCTOR:构造器的声明

FIELD:域声明(包括enum实例)

LOCAL_VARIABLE:局部变量声明

METHOD:方法声明

PACKAGE:包声明

PARAMETER:参数声明

TYPE:类、接口(包括注解类型)或enum声明

@Retention

表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:

SOURCE:注解将被编译器丢弃

CLASS:注解在class文件中可用,但会被VM丢弃

RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。

@Document

将注解包含在Javadoc中

@Inherited

允许子类继承父类中的注解

注解的创建:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation {
    int id();
    String name() default "TestAnnotation";
}

注解的读取:

@TestAnnotation(id = 1)
class AnnotationTest {
    @TestAnnotation(id = 2,name = "MethodAnnotation")
    void testFun() {}
}

// ----------------------------

Class cls = AnnotationTest.class;
TestAnnotation clsAnnotation = (TestAnnotation) cls.getAnnotation(TestAnnotation.class);
System.out.println("clsAnnotation " + clsAnnotation.id() + " " + clsAnnotation.name());

Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
    TestAnnotation mAnnotation = m.getAnnotation(TestAnnotation.class);
    System.out.println("mAnnotation " + mAnnotation.id() + " "+ mAnnotation.name());
}

// 输出
clsAnnotation 1 TestAnnotation
mAnnotation 2 MethodAnnotation

 

说说你对依赖注入的理解

依赖注入的简单理解

这部分我对很多概念的印象还是糊的,以下请谨慎参考。

还是采用上文中的人开车的例子。

第一步:

一个人开丰田车,最基本的写法就是人持有丰田车的引用。

class Person {
    Toyota toyota;

    Person(Toyota toyota) {
        this.toyota = toyota;
    }

    void drive() {
        toyota.run();
    }
}

第二步:

如果这个人不开丰田车了,改开Audi,则需改动代码。改进这一点,就涉及到依赖倒置原则

  • 高层模块不应该依赖低层模块,两者都应该依赖抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象
class GoOut {
    void go() {
        // Toyota类实现了Car接口
        Toyota t = new Toyota();
        Person person = new Person(t);
        person.drive();
    }
}

class Person {
    Car car;

    Person(Car car) {
        this.car = car;
    }

    void drive() {
        car.run();
    }
}

第三步:

经过上一步改进,实现了Person和具体车型的解耦,只需改动GoOut类的代码就可以更换Person的车型。如果要更进一步,可将依赖关系交由专门容器管理,即依赖注入。Spring实现IoC首先需要配置xml文件:

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id="oneCar" class="Toyota"> <!-- Toyota类是ICar的一个实现-->
   </bean>
   <bean id="onePerson" class="Person"> <!--本例以属性方式注入为例 -->
       <property name="car"> 
           <ref bean="oneCar"></ref>
       </property>
   </bean>
</beans>

然后使用 BeanFactroy 类调用:

class GoOut {
    void go() {
        BeanFactory factory=new XmlBeanFactory("bean.xml");
        Person boy=(Person)factory.getBean("onePerson");
        boy.drive();
    }
}

控制反转和依赖注入的理解(通俗易懂)

 

说一下泛型原理,并举例说明

java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

类型擦除

 

 

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读