java基础面试题

本文详细讲解了Java中的String、StringBuffer、StringBuilder的区别,final关键字的用法,List和Set的异同,以及HashMap、LinkedHashSet和TreeSet的特性。还涉及了内存管理(如堆栈和对象分配)、方法重载与重写、静态代码块和构造代码块,以及URI、URL和URN的区别。
摘要由CSDN通过智能技术生成

1、String、StringBuffer、StringBuilder的区别

String

  • String的值不可变,这就导致每次对String的操作都会生成新的String对象
  • 效率低下,而且浪费大量有限的内存空间
  • 线程安全的

StringBuffer

  • StringBuffer是可变类线程安全的字符串操作类,任何对它指向的字符串操作都不会产生新的对象。
  • 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量
    在多线程情况下操作字符串

StringBuilder

  • 可变类、线程不安全(效率比StringBuffer快)
    单线程操作字符串

[参考内容:字符串常量池]

2、String类中里两种对象实例化的区别

直接赋值
例如:String str = "hello";
如果常量池中不存在hello,那么会创建一个对象,并且将引用地址存储到常量池中。因为JVM提供了字符串常量池,位于堆中(JDK1.7以后,1.6在方法区),当使用字面量进行创建字符串时,常量池会对其引用进行保存,后面如果创建重复的字面量(常量池中存在,就不用创建对象了),就会直接将常量池中的引入进行返回。
构造方法
例如:String str = new String("hello);
如果常量池中不存在hello,那么会创建两个对象,也就是在堆中开辟两块内存空间,将字面量对象的引入保存在常量池中,str对象的value保存的是保存在常量池中的引用地址。

3、final关键字

final关键字可以修饰类、方法、局部变量、实例变量等。
[参考内容:final]

4、list和set的区别

在这里插入图片描述

相同点
List和Set都继承自Collection接口
不同点
1、元素的顺序
List是有序集合(按照插入顺序排序),而Set是无序集合(不保证元素的顺序)。
注:元素在Set中的位置是由该元素的HashCode决定的,其位置是固定的
2、元素唯一性
List中可以包含重复元素,Set中没有重复元素。

ArrayList

增删慢、查找快,非线程安全
ArrayList类是一个可以动态修改的数组,与普通数组的却别就是它没有固定大小的限制,我们可以添加或删除元素。ArrayList继承了AbstractList,并实现了List接口。
在这里插入图片描述

方法

public boolean add(E e); // 直接在集合的末尾添加元素数据e
public void add(int index,E e); // 在集合指定index索引位置添加元素数据e 
public E get(int index); // 获得集合总指定index索引位置的元素数据
public E remove(int index); // 删除集合中指定index索引位置的元素数据
public boolean remove(Object obj); // 删除集合中指定的元素数据obj
public E set(int index,E e); // 将集合index索引位置的元素数据修改为e
public int size();  // 获得集合里面元素数据的个数!

[参考内容:ArrayList扩容机制]

LinkedList

添加、删除效率高

在这里插入图片描述
方法

public void addFirst(E e): //将指定元素插入此列表的开头。
public void addLast(E e): // 将指定元素添加到此列表的结尾。
public E getFirst(): // 返回此列表的第一个元素。
public E getLast(): // 返回此列表的最后一个元素。
public E removeFirst(): // 移除并返回此列表的第一个元素。
public E removeLast(): // 移除并返回此列表的最后一个元素。
public E pop(): // 从此列表所表示的堆栈处弹出一个元素。
public void push(E e): // 将元素推入此列表所表示的堆栈。
public boolean isEmpty()// 如果列表不包含元素,则返回true。

HashSet

无序,元素不可重复
HashSet保证元素唯一性的方式依赖于:hsahCode和equals方法。
在这里插入图片描述

HashSet集合存储数据的结构(哈希表)
JDK1.8之前,哈希表底层采用数组+链表实现,即使用数组处理冲突,同一hash值的链表都存储在一个数组里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

问题一:HashMap如何保证key不重复?
HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值.因而具有很快的访问速度,但是遍历顺序却不确定的。
先判断key存放的位置, HashMap通过key的hashCode经过扰动函数处理过后得到 hash 值,然后通过(n-1)&hash 判断当前元素存放的位置(这里的 n 指的是数组的长度).如果当前位置不存在元素的话,直接插入;否则,再通过 key 的 equal()方法判断 key 是否相同,如果相同 value值就会覆盖; 如果 key 的 equals()方法不同,则在数组对应索引位置变为链表存储新的Entry<key,value>。
如果链表的长度超过8则转为红黑树, 当红黑树中的元素小于6时又变为链表(有这些变化的原因就是综合时间复杂度以及空间复杂度的考虑)获取时,直接找到 key 的 hash 值对应的下标,在进一步用 equels 方法判断 key 是否相同,从而找到对应值则返回找不到则返回 null。相比于之前的版本,jdk1.8 在解决哈希冲突时有了较大的变化,当链表长度大于阀值(默认为 8)时,将链表转化为红黑树,以减少搜索时间。

问题二:hashMap 中什么时候需要进行扩容?

  1. 初始化数组 table
  2. 当数组 table 的 size 达到阈值时进行扩容

通过判断旧数组的容量是否大于 0来判断数组是否初始化过。
①如果小于 0:进行初始化,判断是否调用无参构造器。
如果调用了无参构造器: 使用默认的大小和阙值<阈值 12. 阀值=默认大小为 16 乘以负载因子0.75。
如果没有调用无参构造器: 使用构造函数中初始化的容量, 当然这个容量是经过 tableSizefor 计算后的 2 的次幂数) 。
②如果大于 0: 进行扩容,扩容成两倍(小于最大值的情况下),之后在进行将元素重新进行与运算复制到新的散列表中。
结论:扩容需要重新分配一个新数组,新数组是老数组的2倍长,然后遍历整个老数组,把所有的元素挨个重新hash 分配到新数组中去。
如果我们往集合中存放自定义的对象,那么为了保证其唯一,就必须重写hashCode和equals方法简历属于当前对象的比较方式

LinkedHashSet

有序!不可重复!

TreeSet

TreeSet集合是Set接口的一个实现类,底层依赖于TreeMap,是一种基于红黑树的实现,其特点为:

1. 元素唯一(不可重复)
2. 元素没有索引
3. 使用元素的自然顺序对元素进行排序,或者根据创建 TreeSet 时提供的  Comparator  比较器 进行排序,具体取决于使用的构造方法:

5、==和equals之间的关系、区别?

对象类型不同
1、equals():是Object中的方法
2、==:是操作符

比较的对象不同
1、equals():equals是Object中的方法,在Object中equals方法实际上return (this==obj),用到的还是"==",不重写父类的euals方法的话,作用与双等号相同,还是比较的是地址值。
在这里插入图片描述

2、==:用于比较引用和比较基本数据类型时具有不同的功能,比较引用数据类型时,如果该对象没有重写equals方法,则比较的是地址值。
基本数据类型只能用来比较两个值是否相同。

6、基本类型和引用类型

基本类型
基本数据类型有8种,如下分类
1、整数类型:byte、short、int、long
2、浮点类型:float、double
3、字符类型:char
4、布尔类型:boolean

引用类型
引用数据类型包括:类、接口、数组、枚举、注解、字符串

基本类型和引用数据类型的区别
1、基本类型存储在
2、引用类型的具体内容存储在中,引用的地址存储在

7、自动装箱、拆箱的概念

装箱:
把基础数据类型转换成引用类类型
例如:Integer num = 1;
拆箱:
把引用类型转换为对应的基础类型
例如:int number = num;
[参考内容:装箱和拆箱]

8、反射的概念

反射是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查。被private封装的资源只能类内部访问,外部是不行的,但反射能直接操作类私有属性。反射可以在运行时获取一个类的所有信息,(包括成员变量、成员方法、构造器等),并且可以操作类的字段、方法、构造器等部分。
在这里插入图片描述
结论:我们使用反射,其实就是获得某个类的Class对象,然后通过这个Class对象对这个类中的成员数据进行处理(调用执行)!

使用反射开发常用的固定步骤
①获得类的Class对象(有三种方式)【Class clazz】
Class.forName(“全限定类名”);
类名.class;
类的对象.getClass();

②创建类的实例对象(2种操作)
clazz.newInstance(); // 调用公有的无参构造方法 【不推荐使用,但是可以用!】
clazz.getDeclaredConstructor().newInstance(); // 通过构造方法对象创建对象的!

③通过clazz对象创建指定方法的Method对象或者指定属性名称的Field对象(很少操作Field)
Method method = clazz.getMethod(“方法名称”,方法的参数类型); // 得到Metho对象method
④使用method对象调用invoke方法,执行这个指定的方法!
Obejct obj = method.invoke(Object obj,T…parameter); // 指定这个方法是哪个对象的,给定这个方法的实际参数,得到这个方法执行的结果obj

9、堆栈之间的关系

1、是线程私有的,每个线程运行时都会创建一个运行时栈,栈区包含基本的数据类型(所有的①局部变量在栈内存中创建)以及②对象的引用、③存放线程调用方法时存储局部变量表,操作,方法出口等方法执行相关的信息。
2、:所有的对象和它们相应的实例变量以及数据被存储在这里。每个JVM同样只有一个堆区。由于方法区和堆区的内存由多个线程共享,所以存储的数据不是线程安全的

10、方法的重载和重写

方法重载
多个方法在同一个类中,方法名称相同,参数列表不同!
方法重写
1.多个方法必须在不同的类(必须存在继承或者实现的关系)中
2.整个方法签名一样的(子类与父类的方法)
3.权限修饰符,子类的范围不得低于父类!
权限修饰符: public > protected > default(默认,空着不写) > private

11、静态代码块

什么是静态代码块?

static {
    // 静态代码块的代码逻辑
}

1、用static关键词修饰声明,静态代码块是在类加载阶段执行的,优先于其他代码块和构造方法。
2、静态代码块只会执行一次
3、静态代码块可以用来初始化静态变量,进行一些静态济源的初始化操作。
4、静态代码块不能直接访问非静态成员(实例变量、实例方法),但可以访问静态成员(静态变量、静态方法)
5、多个静态代码块按照定义的顺序依次执行。

什么是构造代码块?

{
		// 类中方法外
}

1、直接在类中定义,并且没有用static声明的代码块。
2、在创建对象时被调用,每次创建对象都会被调用
2、执行次序:在静态代码块之后,在构造方法之前。
3、执行顺序:有多个静态代码块,按照定义的顺序执行。
1.3 什么是普通代码块?
普通代码块是在方法体中定义的。

1.4 构造方法
1、方法名与类名完全相同。
2、创建对象时自动调用。

12、什么是URI、URL、URN

URI: Uniform Resource Identifier,统一资源标识符,用来说明这个资源是个什么东西。
URL: Uniform Resource Locator,统一资源定位符,表示这个资源在网络上的位置。
问题:当资源换了位置存储之后,之前的URL就失效了
URN: Uniform Resource Name,统一资源命名符,URN一般用一个独一无二的字符串来表示资源,通过对字符串的解析,可以得到资源最新的地址,但是这需要一个解析器,解析器的实现很麻烦,所以一般还是使用URL。
在这里插入图片描述

13、权限修饰符

Java中提供了四种访问权限修饰符
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值