JavaSE部分之(1)Java基础


JavaSE部分之(1)Java基础

1、为什么重写equals还要重写hashcode
为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,那就没必要再进行equals的比较了,这样就大大减少了equals比较的次数,在需要进行大量比较的情况下可以显著的提高效率,一个很好的例子就是在集合中的使用;我们都知道java中的List集合是有序的、可以重复的,而set集合是无序的、不能重复的。如果单靠equals方法比较的话,假设原来集合中已经有10000个元素了,那么放入第10001个元素时,要将前面所有的元素都进行equals比较,看看是否有重复,这个效率就会很慢,因此hashcode应运而生,java采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,如果有,再用equals比较,如果没有则直接插入,这样就大大减少了equals的使用次数,执行效率就大大提高了。

简单的说就是为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确不相同,这样就违背了Java API中对hashCode方法的描述。
(打个比方,一个名叫张三的人去住酒店,在前台登记完名字就去了99层100号房间,此时警察来前台找叫张三的这个人住在哪间房,经过查询,该酒店住宿的有50个叫张三的,需要遍历查询,查询起来很不方便。
那么就换另外一种登记方式,前台登记时登记身份证号,警察来前台找身份证号时发现身份证号也存在重复,经过哈希算法进行计算后相同的hashcode值被分到了一个房间然后产生链表,链表查询效率非常慢,然后警察找的时候也会遇到问题。
那么只能换第三种登记方式了,前台登记时同时登记身份证号和名字,这样警察来找的时候同时按照两个条件去查,这样就能直接锁定要找的人在哪个房间。
在程序中:登记的前台好比哈希算法,名字是对比好比 equals 方法,身份证号的对比好比 hashcode 方法只有equals 和 hashcode 都满足的时候才能确保是同一个对象。)

当我们重写一个类的 equals 方法时就应当连同重写 hashcode 方法,并且两个方法应满足:
a:一致性,即:当两个对象 equals 比较为 true,那么 hashcode 值应当相等,反之亦然,因为当两个对象hashcode 值相等,但是 equals 比较为 false,那么在 HashMap 中会产生链表,影响查询性能。
b:成对重写,即重写 equals 就应当重写 hashcode。

 

2、说一下map的分类和常见的情况
java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMap。Map主要用于存储健值对,不允许键重复(重复了会覆盖掉),但允许值重复。

Hashmap是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的;HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致;如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

Hashtable与HashMap类似,不同的是,它不允许记录的键或者值为空,支持线程同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtable在写入时会比较慢。

LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在遍历LinkedHashMap时,先得到的记录肯定是先插入的。在遍历的时候会比HashMap慢;不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

TreeMap实现了SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序比较器,遍历得到的记录是排过序的。

一般情况下,我们用的最多的是HashMap,在Map中插入、删除和定位元素,HashMap是最好的选择;但如果想要按自然顺序或自定义顺序遍历键,那么TreeMap会更好;如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现。

 

3、Object若不重写hashCode()的话,hashCode()如何计算出来的?
Object的hashcode方法是本地方法,也就是用c语言或c++实现的,该方法返回的是一个由内存地址经过散列得到的整数值。

 

4、==比较的是什么?
==比较的是两个对象的内存地址是否相同。


5、若对一个类不重写,它的equals()方法是如何比较的?
和==比较是一样的,都是比较两个对象的内存地址是否相同。

 

6、java8新特性
1.Lambda表达式
2.接口的默认方法与静态方法
3.方法引用
4.重复注解
5.扩展注解的支持
6.Optional类
7.Stream API
8.Date Time API
9.JavaScript引擎Nashorn
10.Base64

 

7、说说Lamda表达式的优缺点。
优点:
  简洁
  非常容易并行计算
  可能代表未来的编程趋势

缺点:
  可读性不是很强
  若不用并行计算,很多时候计算速度没有比传统的for循环快
  不易调试

参考:https://blog.csdn.net/robert_chen1988/article/details/78508322

 

8、一个十进制的数在内存中是怎么存的?
是以二进制补码形式存储的

 

9、为啥有时会出现4.0-3.6=0.40000001这种现象?
在二进制系统中无法精确地表示分数1/10,这就好像十进制无法精确地表示分数1/3一样。
如果在数值计算中不允许有任何舍入误差 ,就应该使用BigDecimal类。

 

10、Java支持的数据类型有哪些?什么是自动拆装箱?
八个基本数据类型:byte,short,int,long,float,double,char,boolean;以及引用类型。

整数默认int型,小数默认是double型,float、long类型必须加后缀f、l;

自动装箱和拆箱就是基本类型和其对应引用类型之间的转换,
自动装箱: 就是将基本数据类型自动转换成对应的包装类,
自动拆箱:就是将包装类自动转换成对应的基本数据类型,
基本类型转换为引用类型后,就可以直接调用包装类中封装好的一些方法。

详见:
https://blog.csdn.net/wufaliang003/article/details/82347077

 

11、什么是值传递和引用传递?
值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。

引用传递:也称为传地址。方法调用时,实际参数的引用被传递给方法中对应的形式参数,方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。

 

12、数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?
区别:
数组的大小是固定的,列表的大小是动态变化的;
数组在声明的同时必须进行实例化(至少得初始化数组的大小),列表可以只是先声明,之后再进行实例化;
数组可以存储基本类型和对象类型,列表只能存储对象类型(存储基本类型要用包装类);
数组只能存储同一类型的数据,列表可以存放不同类型的数据(在没有声明泛型具体类型的情况下);

使用选择:
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,可以使用一个全局数组;
如果只是单纯想要以数组的形式保存数据,而不对数据进行增加、删除等操作,只是为了方便进行查找的话,可以使用ArrayList,如果需要对元素进行频繁的移动或删除,或者是处理超大量的数据,使用ArrayList就不合适了,因为它的效率很低,可以选择使用LinkedList。

 

13、你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?

大O符号描述了当数据结构里的元素增加的时候,算法的规模或性能在最坏的情况下有多好。

比如数组的插入时间复杂度为O(N),空间复杂度为O(1);链表的插入时间复杂度为O(1),空间复杂度为O(1)。

 

14、String是最基本的数据类型吗?
不是,String是引用类型;String类是final的,不能被继承,不能被修改;

Java的基本类型只有八个:byte(1字节)、short(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节)、char(2字节)、boolean。

 

15、int 和 Integer 有什么区别
int是Java的基本类型,Integer是int的包装类,是引用类型;
int的默认值为0,Ingeter的默认值为null。

java在编译Integer i = 128的时候,被翻译成Integer i = Integer.valueOf(128),而valueOf()函数会对-128到127之间的数进行缓存,如果在缓存中,就不会新建一个对象,否则,新建一个对象;

同样适用于Byte、Short、Integer、Long、Character,其中Character缓存的是0到127之间的数;

1 public class Java8Tester {
2   public static void main(String args[]) {
3     Integer i1 = 127;
4     Integer i2 = 127;
5     System.out.println(i1==i2);
6   }
7 }//结果为:true

 

1 public class Java8Tester {
2   public static void main(String args[]) {
3     Integer i1 = 128;
4     Integer i2 = 128;
5     System.out.println(i1==i2);
6   }
7 }//结果为:false


int类型和Integer类型比较,会把Integer自动拆箱为int再去比,所以,只要值相同,两者就是相同的。

1 public class Java8Tester {
2   public static void main(String args[]) {
3     Integer i1 = 99999;
4     int i2 = 99999;
5     System.out.println(i1==i2);
6   }
7 }//结果为:true


16、String 和StringBuffer的区别
String类是final的,不可变(这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间),
StringBuilder和StringBuffer(线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量)可变;
大部分情况下的执行效率:StringBuilder > StringBuffer > String,但是类似String str = "abc"+"def"这种情况下,String的效率最高;
String是线程安全的,因为String类是final的,StringBuffer也是线程安全的,StringBuilder不是线程安全的。

总结一下:
String适用于少量字符串操作的情况;
StringBuilder适用于单线程下进行大量操作的情况;
StringBuffer适用于多线程下进行大量操作的情况。

StringBuilder线程不安全验证:
https://blog.csdn.net/weixin_34062469/article/details/86825792

 

17、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?

 1 public class Java8Tester {
 2   public String translate(String str) {
 3     String tempStr = "";
 4     try {
 5       //按照ISO-8859-1进行解码
 6       byte[] b = str.getBytes("ISO-8859-1");
 7       //按照GBK重新进行编码 
 8       tempStr = new String(b, "GBK");
 9       tempStr = tempStr.trim();
10     } catch (UnsupportedEncodingException e) {
11       e.printStackTrace();
12     }
13     return tempStr;
14   }
15 }

 

 

18、&和&&的区别?
相同点:
&和&&都可以用作逻辑与的运算符,表示逻辑与

不同点:
&不具备短路功能,&两边都要计算,而&&具有短路功能,即&&左边为false结果就为false,右边不再进行计算;
&还可以表示按位与操作,而&&不行。

 

19、在Java中,如何跳出当前的多重嵌套循环?
java中使用带有标号的break语句跳出多重循环,即在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出。

 

20、你能比较一下Java和JavaSciprt吗?
java:面向对象,需要编译,再进行运行,强类型;

javascript:基于对象和事件驱动,解释型语言,弱类型。

 

21、简述正则表达式及其用途。
在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

 

22、Java中是如何支持正则表达式操作的?
Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作。


扩展:
字符串方法replace()、replaceAll()、replaceFirst()的区别和使用方法?

1 String s = "my.test.txt";
2 System.out.println(s.replace(".", "#"));
3 System.out.println(s.replaceAll(".", "#"));
4 System.out.println(s.replaceFirst(".", "#"));

运行结果:
my#test#txt
###########
#y.test.txt

解析:
“.”是正则表达式的元字符,匹配除换行符以外的任意字符,所以replaceAll()、replaceFirst()才出现了意想不到的结果。
而replace()没有用到正则表达式,但会把所有“.”替换掉,很多人可能会误解replace()是替换单个,而replaceAll是替换全部,
其实这是错的(我以前也是这么想的- -)。replace()只是没有用到正则表达式,但会替换所有匹配的字符串。
到这里一些不懂正则表达式的小伙伴可能就要喊坑爹了,“那我不想用正则表达式去替换第一个字符串肿么办?”
其实也很简单,只要将元字符串转义就行了。

1 String s = "my.test.txt";
2 System.out.println(s.replace(".", "#"));
3 System.out.println(s.replaceAll("\\.", "#"));
4 System.out.println(s.replaceFirst("\\.", "#"));

运行结果:
my#test#txt
my#test#txt
my#test.txt

 

23、请你说说Java和PHP的区别?

Java是纯面向对象的语言,是编译型强类型语言;

PHP既可面向对象,又可面向过程,是解释型弱类型语言。


作者:尘事如霜人如水,发布于:2019/05/16
原文:https://www.cnblogs.com/xu-xiaolong/p/10875959.html