Java面试中的各种问题

版权声明:本文为博主原创文章,转载请注明作者和出处,如有错误,望不吝赐教。 https://blog.csdn.net/weixin_41888813/article/details/83824982

1,String、StringBuffer、StringBuilder的区别

解答:

  • String的对象是字符串常量,如果做大量的字符串拼接效率会很低下。因为Java中对String对象进行的操作实际上是一个不断创建新对象并且把旧对象回收的一个过程,虚拟机需要不断的将对象引用指向新的地址。
  • 而StringBuffer和StringBuilder的对象是字符串变量,StringBuilder速度比StringBuffer更快。在线程安全上,StringBuffer中很多方法带有synchronized关键字,可以保证线程是安全的;而StringBuilder中的方法没有synchronized关键字,线程是不安全的。

使用场景小结:

  • String:适用于少量的字符串操作的情况;
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况;
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况;
  • 一般方法内的私有变量推荐使用StringBuilder,如果是多线程需要同步的话就选用SringBuffer。

2,对参数为做空验证,就做判断值相等

Java面试中遇到的坑【填坑篇】

下面的写法将常量放到方法左边,能防止NPE。

Java面试中遇到的坑【填坑篇】

解答:

  • null的值调用equals方法与其他值做比较的时候,会导致抛出空指针异常。因为null值,并不是一个String对象,自然不能调用String的实例方法equals
  • 在源码中如果anobject为空的时候,调用equals方法只会返回false,并不会出现NPE
    public boolean equals(Object anObject) {
        //首先,先去判断两个对象是否具有相同的地址(是否同一个对象的引用)
        if (this == anObject) {
            return true;
        }
        //如果地址不一样,则证明不是引用同一个对象,接下来就是挨个去比较两个字符串对象的内容是否一致
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = count;
            if (n == anotherString.count) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = offset;
                int j = anotherString.offset;
                while (n-- != 0) {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }
        }
        return false;
    }

 小例子:

假设两个字符串

  1. String  a = "hello" 这里Java的机制首先在常量池里面创建这个hello字符串,然后在内存里面创建一个地址,引用常量池里面的这个hello值
  2. String b = null;  这里或者 String b;  这里两行申请  Java在内存里面都申请了一个地址,但是不指向任何引用地址,或者说他没有内容'

小结:

  1. s.equals(s1),s必须非null,否则会空指针异常;
  2. s1可以为null,在确保s1不为空时,可以用s1.equals(s)来判断;
  3. 无法确定s或s1不为null时,解决办法:
  4. s != null && s.equals(s1)

3,Integer类型正整数在小于128时和大于128时

问题:

Java面试中遇到的坑【填坑篇】

打印结果为false,而下面结果为true为啥

Java面试中遇到的坑【填坑篇】

解答:

  • Integer类型当正整数小于128时是在内存栈中创建值的,并将对象指向这个值,这样当比较两个栈引用时因为是同一地址引用两者则相等。
  • 当大于127时将会调用new Integer(),两个整数对象地址引用不相等了。
  • 这就是为什么当值为128时不相等,当值为100时相等了。

小结(==与equals的区别):

  • == 只针对变量的值;equals只针对对象的内容
  • 引用类型的变量值是所指对象的内存地址

4,将变量和对象作为参数传递的区别

问题:将变量作为参数传递,在方法中改变参数值,变量的值改变了么?下图total值到底是几?

Java面试中遇到的坑【填坑篇】

解答:

  • 将一个私有变量作为形参传递赋值并不会改变参数原有的值
  • 但是如果将一个对象作为参数传递改变属性,对象的属性值就会随着改变
  • 因此total的值仍然为0。

将一个对象作为参数传递时:

public class AtWill {

	public static void main(String[] args) {
		
		NumObject nb = new NumObject(10, "法拉利");
		paramCheck(nb);
		System.out.println(nb.toString());
	}
	
	private static void paramCheck(NumObject nb) {
		nb.setName("宾利");
	}
}

result

NumObject [total=10, name=宾利]

5,数组中内部定义的一种数据结构类型

问题:由数组转换的list,只能循环遍历,而不能看长度,增加元素,删除元素,这是为何?下图代码执行竟然出错!

Java面试中遇到的坑【填坑篇】

解答:

  • 因为将数组转换的列表其实不是我们经常使用的arrayList
  • 这只是数组中内部定义的一种数据结构类型,本质还是原数组而并非列表
  • 因此当向列表添加元素就会出现错误

小结:

对于Arrays.asList()方法需要注意以下几点:

  1. 该方法返回的是基于数组的List视图(List view)。所以,这种方式是将数组转换为List的最快的方式。因为返回的只是视图,不需要多余的内存来创建新的List以及复制操作。
  2. 该方法返回的List是长度是固定的(fixed),不是只读的。所以我们不能进行删除、添加操作,而可以使用set()方法进行修改元素操作。如果你对返回的List执行add()添加新元素,会返回UnsupportedOperationException。(原因:Arrays.asList()方法返回的是个内部的ArrayList,而这种ArrayList根本就没有add()和remove()方法,但有set()方法)
  3. 因为该方法返回的是基于原数组的List视图,所以,当我们使用set方法修改了List中的元素的时候,那么原来的数组也会跟着改变(这是视图的特性)。
  4. 从java 5开始,该方法支持泛型,所以我们可以从数组中得到类型安全ArrayList。

注意:

  • 如果我们想让转换为只读的List,可以使用Collections.unmodifiableList()方法来将数组转换为指定List。
  • 如果想返回的方法能够进行添加、删除元素操作,则可以使用new ArrayList(Arrays.asList(array)) ,这样就会创建一个对象类型的ArrayList,并将数组的内容拷贝过去。

6,遍历过程中移除元素,导致的越界错误

问题:将列表中李明的名字移除掉,下图实现有无问题?

Java面试中遇到的坑【填坑篇】

解答:

  • 在列表中移除最后一个元素按说应该没有问题的,但是这个算法还是出现了错误
  • 主要是这种写法的列表循环(加强for循环)遵循下表索引查找,当移除某个元素时,上次计算出来的长度超过了当前列表长度,故而会出现越界错误。

另一种循环方法(可以看见remove方法后,列表的长度实时的减一了):

public class AtWill {

	public static void main(String[] args) {
		
		List<String> list = new ArrayList();
		String removName = "李明";
		list.add("张三");
		list.add("李四");
		list.add("李明");
		list.add("李明");
		list.add("赵六");
		System.out.println(list);
		System.out.println("list的长度"+list.size());
		for(int i=0; i<list.size(); i++) {
			if(removName.equals(list.get(i))) {
				list.remove(list.get(i));
                                //索引往回一个单位
				i--;
                                //列表的长度实时的减一了
				System.out.println("list当前的长度"+list.size());
			}	
		}
		System.out.println(list);
	}
}


7,mkdir创建目录

问题:在指定目录下创建文件目录,到底使用哪一种呢,两个方法都没报错,为何第一次没创建目录而第二次创建?

Java面试中遇到的坑【填坑篇】

解答:

  • mkdir()只会建立一级的文件夹
  • mkdirs()可以建立多级文件夹

8,float类型数据相减会丢失精度

Java面试中遇到的坑【填坑篇】

解答:

  • 两个float类型数据相减会丢失精度,尾部带着常常的一串数字。
  • 如果实际场景要做计算有这么两种思路:
  • 第一先将单位做成整数再做除法;
  • 第二可以用bigdecimal来计算(用来对超过16位有效位的数进行精确的运算)(点击传送到BigDecimal )
public class AtWill {
	public static void main(String[] args) {	
		Float totalMoney = 200000.8f;
		Float ownMoney = 170000.5f;
		
		//不能直接相减,会损失精度,错误X
//		Float leftMoney = totalMoney - ownMoney;
		
		//第一种合理的做法,先将单位做成整数再做除法(思路)
		Float leftMoney = 0f;
		process(totalMoney,ownMoney);	
		System.out.println("还剩余:"+leftMoney);
	}	
	public static Float process(Float totalMoney, Float ownMoney) {
		int change = (int)(totalMoney*10) - (int)(ownMoney*10);
		int remainder = change%10;
		return (float)(change/10) + (float)remainder/10;
	}
}

9,创建线程的方式(点击传送到Java中创建线程的三种方式以及区别)

问题:

面试者:继承Thread类和实现runnable接口!
面试官:除此以外还有方法么?
面试者:就这两种实现啊?!
面试官:我们聊点别的,呵呵。

解答:

  • 其实 实现多线程还可以实现Callable接口,利用task来接受异步线程的执行结果。
  • 希望后面再被面试官问到这道题可千万别再入坑,并且能回答这第三种方式跟前两种不同的地方(可以获取执行结果)。

参考来源于:

https://www.cnblogs.com/TTTTT/p/5905440.html

https://zhidao.baidu.com/question/326494195772278445.html

https://blog.csdn.net/darxin/article/details/5247391

https://www.cnblogs.com/GarfieldEr007/p/7082945.html

https://www.cnblogs.com/kniught-ice/p/4755122.html

猜你喜欢

转载自blog.csdn.net/weixin_41888813/article/details/83824982