大数据技术之_31_Java 面试题_02_== 和 equals 有什么区别 + String 相关 + 多态 + 传值 + static 加载机制 + 线程

本文主要是介绍大数据技术之_31_Java 面试题_02_== 和 equals 有什么区别 + String 相关 + 多态 + 传值 + static 加载机制 + 线程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大数据技术之_31_Java 面试题_02

      • 1、== 和 equals 有什么区别?
      • 2、为什么需要同时覆写 hashCode 和 equals 方法?
      • 3、为什么用 eclipse 重写 hashCode 方法,有 31 这个数字?
      • 4、String 相关
      • 5、多态
      • 6、传值
      • 7、static 加载机制
      • 8、谈谈你对 HashMap 中 put/get 方法的认识?如果了解再谈谈 HashMap 的扩容机制?默认大小是多少?什么是负载因子?什么是吞吐临界值?JDK1.7 版本为例
      • 9、请问 ArrayList/LinkedList/Vector 的区别?谈谈你的理解?ArrayList 底层是什么?扩容机制?Vector 和 ArrayList 的最大区别?JDK1.7
      • 10、线程

程序员级别:码龙 > 码神 > 码农 > 码畜
学生级别:学神 > 学霸 > 学渣 > 学弱

IT/DT 是脑力密集型的高智商行业。

反复的强化,反复的强化,反复的强化。
刻意的练习,刻意的练习,刻意的练习。

1、== 和 equals 有什么区别?

== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址(值)(本质上来说也是值)。equals 的话,它是属于 java.lang.Object 类里面的方法,在源代码的 149 行,如果该方法没有被重写过默认也是 ==,我们可以看到 String 类的 equals 方法是被重写过的,而且 String 类在日常开发中用的比较多,久而久之,形成了 equals 是比较值的错误观点(是因为覆写了 equals 方法才比较值)。具体要看这有没有重写 Object 的 hashCode 方法和 equals 方法来判断。-----------------------------------------------------------------以 Person 为例,何时需要重写 equals()?当一个类有自己特有的“逻辑相等”概念,当改写 equals() 的时候,总是要改写 hashCode(),根据一个类的 equals 方法(重写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据 Object.hashCode 方法,它们仅仅是两个对象。因此,违反了 “相等的对象必须具有相等的散列码”。结论:重写 equals 方法的时候一般都需要同时重写 hashCode 方法。

示例代码如下:

package com.atguigu.test;import java.util.HashSet;
import java.util.Set;import com.atguigu.entities.Person;public class TestEquals {public static void main(String[] args) {// String s1 = new String("abc");// String s2 = new String("abc");// System.out.println(s1 == s2); // false// System.out.println(s1.equals(s2));//// Set<String> set01 = new HashSet<String>();// set01.add(s1);// set01.add(s2);// System.out.println(s1.hashCode() + "\t" + s2.hashCode());// System.out.println(set01.size());System.out.println("================================");// 两个截然不同的实例,有可能在逻辑上是相等的// (假设项目需要,自定义逻辑相等的概念,比如只要 name 属性值一致我们就是认为是同一个对象)// 即只要 name 属性值一致,用 equals 比较得到 true,就是同一个对象,这是开发人员自己定制的业务规则,但是JVM 不认,因为 JVM 只认识 hashCodePerson p1 = new Person("abc");Person p2 = new Person("abc");System.out.println(p1 == p2); // falseSystem.out.println(p1.equals(p2));Set<Person> set02 = new HashSet<Person>();set02.add(p1);set02.add(p2);System.out.println(p1.hashCode() + "\t" + p2.hashCode());System.out.println(set02.size());}
}

2、为什么需要同时覆写 hashCode 和 equals 方法?

答:因为假如一个类被用在集合类中,在该集合类中判断是否为同一个对象,判断的是 hashCode 的值(即地址值)。即仅仅覆写 equals 方法是不够的!

3、为什么用 eclipse 重写 hashCode 方法,有 31 这个数字?

  计算机的乘法涉及到移位计算。当一个数乘以 2 时,就直接拿该数左移一位即可!选择 31 原因是因为 31 是一个素数!所谓素数:质数又称素数(在一个大于 1 的自然数中,除了 1 和此整数自身外,没法被其他自然数整除的数)。

  在存储数据计算 hash 地址的时候,我们希望尽量减少有同样的 hash 地址,所谓 “冲突”。

  因为任何数 n * 31 就可以被 JVM 优化为 (n << 5) -n,移位和减法的操作效率要比乘法的操作效率高的多,对左移虚拟机里面都有做相关优化,并且 31 只占用 5 bits!

4、String 相关

示例代码如下:

package com.atguigu.test;public class TestString {public static void main(String[] args) {String s1 = new String("abc"); // 生成了两个对象:一个在字符串常量池中,一个在堆中被 s1 指向String s2 = "abc"; // s2 指向字符串常量池的 "abc"String s3 = new String("abc"); // 生成了一个对象:s3 指向堆中另一个 "abc"System.out.println(s1 == s2); // falseSystem.out.println(s1 == s3); // falseSystem.out.println(s2 == s3); // falseSystem.out.println("====================");// String s2 = "abc"; // s2 指向字符串常量池的 "abc"// String s1 = new String("abc"); // 生成了一个对象:s2 指向堆中一个 "abc"// String s3 = new String("abc"); // 生成了一个对象:s3 指向堆中另一个 "abc"//// System.out.println(s1 == s2); // false// System.out.println(s1 == s3); // false// System.out.println(s2 == s3); // false//// System.out.println("====================");/** 返回字符串对象的规范化表示形式。 一个初始为空的字符串池,它由类 String 私有地维护。 * 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用  equals(Object) 方法确定),* 则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。 * 它遵循以下规则:对于任意两个字符串  s 和  t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。* 所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。* 字符串字面值在 Java Language Specification 的 §3.10.5 定义。 * 返回:一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。*/System.out.println(s1 == s1.intern()); // falseSystem.out.println(s2 == s2.intern()); // trueSystem.out.println(s1.intern() == s2.intern()); // true    简言之:intern() 表示找池中常量的地址System.out.println("====================");String s4 = "java";String s5 = "ja";String s6 = "va";System.out.println(s4 == "java"); 		// true 	常量找池(池对象)System.out.println(s4 == (s5 + s6)); 	// false	拼接找堆(会在堆中生成新的对象,堆对象)System.out.println(s4 == "ja" + s6); 	// false}
}

5、多态

什么是多态?
Java 里通过方法重载和方法重写来体现多态是否正确?答:错误,方法重载跟多态没有任何关系。
多态是编译时行为还是运行时行为?答:运行时行为。因为只有在实际调用运行时才能确定具体的对象。

示例代码如下:

package com.atguigu.test;import java.util.Random;interface Animal {public void eat();
}class Dog implements Animal {@Overridepublic void eat() {System.out.println("dog eat bone---111");}
}class Cat implements Animal {@Overridepublic void eat() {System.out.println("cat eat fish---222");}
}class Sheep implements Animal {@Overridepublic void eat() {System.out.println("sheep eat grass---333");}
}public class TestPolymorphism {public static Animal getInstance(int key) {Animal result = null;switch (key) {case 0:result = new Dog();break;case 1:result = new Cat();break;default:result = new Sheep();break;}return result;}public static void main(String[] args) {Animal animal = TestPolymorphism.getInstance(new Random().nextInt(3));animal.eat();}
}

6、传值

示例代码如下:

package com.atguigu.test;import com.atguigu.entities.Person;public class TestTransferValue {public void changeValue1(int age) {age = 30;}public void changeValue2(Person person) {person.setPersonName("xxx");}public void changeValue3(String str) {str = "xxx";}public static void main(String[] args) {TestTransferValue test = new TestTransferValue();int age = 20;test.changeValue1(age);System.out.println("age---" + age); // age---20Person person = new Person("abc");test.changeValue2(person);System.out.println("personName---" + person.getPersonName()); // personName---xxxString str = "abc";test.changeValue3(str);System.out.println("String---" + str); // String---abc}
}

7、static 加载机制

示例代码如下:

package com.atguigu.test;class Father {public Father() {System.out.println("111111");}{ // 非静态代码块每次实例化的时候都加载System.out.println("222222");}static { // 静态的东西只加载一次System.out.println("333333");}
}class Son extends Father {public Son() {System.out.println("444444");}{ // 非静态代码块每次实例化的时候都加载System.out.println("555555");}static { // 静态的东西只加载一次System.out.println("666666");}
}public class TestStaticSeq {public static void main(String[] args) {// 特别注意:最开始加载静态的内容,有无以下创建对象都会加载(因为静态内容属于类的东西)new Son();System.out.println("-------------------");new Son();System.out.println("-------------------");new Father();}
}

输出结果如下如下:

333333
666666
222222
111111
555555
444444
-------------------
222222
111111
555555
444444
-------------------
222222
111111

口诀static 加载机制:由父到子,静态先行,子方法先行,非静态代码块先于构造方法,构造方法最后

8、谈谈你对 HashMap 中 put/get 方法的认识?如果了解再谈谈 HashMap 的扩容机制?默认大小是多少?什么是负载因子?什么是吞吐临界值?JDK1.7 版本为例

1、HashSet 底层是采用 HashMap 实现
2、集合里面放置的永远是对象的引用而不是对象本身
3、当你在 HashSet 里 add 对象的时候,实际是 HashMap 里面 put 了 key-value 键值对,其中 key 就是你 add 进来的对象,value 是一个固定的 Object 常量
4、HashMap 底层是个 Entry 类型的,名字叫 table 的数组
5、put:当程序试图将一个 key-value 对放入 HashMap 中时,程序首先根据该 key 的 hashCode() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,则新添加 Entry 的 value 将覆盖集合中原有 Entry 的 value,但 key 不会覆盖。如果这两个Entry 的 key 通过 equals 比较返回 false,则新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部--具体说明继续看 addEntry() 方法的说明。

顺便复习下 hashCode、equals、HashSet、HashMap 之间到底有什么样的关系?

[外链图片转存失败(img-RQnxYpWu-1569159499085)(https://s2.ax1x.com/2019/06/17/VHfHn1.png)]

9、请问 ArrayList/LinkedList/Vector 的区别?谈谈你的理解?ArrayList 底层是什么?扩容机制?Vector 和 ArrayList 的最大区别?JDK1.7

ArrayList 定义
[外链图片转存失败(img-gFyYjPBE-1569159499086)(https://s2.ax1x.com/2019/06/17/VHfTXR.png)]

ArrayList/LinkedList/Vector的区别
[外链图片转存失败(img-yhTmCtwW-1569159499086)(https://s2.ax1x.com/2019/06/17/VHfoc9.png)]

10、线程

java8 中的 JUC = java.util.concurrent

题目1:3 个售票员卖出 30 张票 卖票。
示例代码如下:

package com.atguigu.thread;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; // 可重入锁/*** 题目1:3 个售票员卖出 30 张票 卖票 * synchronized: 锁提供了对共享资源的独占访问,一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。* * 1	线程	   操作   资源类 * 2	高内聚+低耦合* * java8 中的 JUC = java.util.concurrent*/
public class ThreadDemo01 {public static void main(String[] args) {Ticket ticket = new Ticket();// 实际开发中使用 匿名内部类 的方式(代码冗余度下降)
//		new Thread(new Runnable() {
//			@Override
//			public void run() {
//				for (int i = 1; i <= 40; i++) {
//					ticket.sale();
//				}
//			}
//		}, "AA").start();// java 8 中使用 lambda 表达式代替 匿名内部类new Thread(() -> {for (int i = 1; i <= 40; i++) {ticket.sale();}}, "AA").start();new Thread(() -> {for (int i = 1; i <= 40; i++) {ticket.sale();}}, "BB").start();new Thread(() -> {for (int i = 1; i <= 40; i++) {ticket.sale();} // 联想到 Scala 的 map 操作}, "CC").start();}
}class Ticket implements Runnable {private int number = 30;private Lock lock = new ReentrantLock(); // Lock 代替 synchronized,但 Lock 更强大public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "\t" + (number--) + "\t 还剩下:" + number);}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}@Overridepublic void run() {// TODO Auto-generated method stub}
}

多线程的三种方式:

  • 第一种:Ticket extend Thread {},此法最搓,强烈不推荐!因为 Java 中要少用继承,继承是很宝贵的资源,尽量要面向接口编程。
  • 第二种:Ticket implements Runnable {} 实现 run() 方法,面向接口编程。不够好,使得 Ticket 和 Runnable 有关系了,没有实现 高内聚+低耦合。
  • 第三种:使用 java.util.concurrent.locks.Lock; (JUC 中的 Lock 接口,实现使用匿名内部类的方式),非常好,实现了 高内聚+低耦合+低冗余度。

题目2:两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来 10 轮。
示例代码如下:

package com.atguigu.thread;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** 题目2:两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来 10 轮* * 1 线程   操作   资源类 * 2 高内聚+低耦合*/
public class ThreadDemo02 {public static void main(String[] args) {// 新建资源类对象ShareData sd = new ShareData();// 线程操作资源类的方法new Thread(() -> {for (int i = 1; i <= 10; i++) {try {Thread.sleep(200);sd.increment();} catch (Exception e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {Thread.sleep(300);sd.decrement();} catch (Exception e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {Thread.sleep(400);sd.increment();} catch (Exception e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 1; i <= 10; i++) {try {Thread.sleep(500);sd.decrement();} catch (Exception e) {e.printStackTrace();}}}, "D").start();}
}// 资源类
class ShareData {private int number = 0;private Lock lock = new ReentrantLock(); // Lock 取代了  synchronizedprivate Condition condition = lock.newCondition(); // Condition 取代了 wait,notify,notifyAllpublic void increment() throws InterruptedException {lock.lock();try {while (number != 0) {condition.await(); // this.wait();}// 干活++number;System.out.println(Thread.currentThread().getName() + "\t" + number);// 通知唤醒condition.signalAll(); // this.notifyAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void decrement() throws InterruptedException {lock.lock();try {while (number == 0) {condition.await(); // this.wait();}// 干活--number;System.out.println(Thread.currentThread().getName() + "\t" + number);// 通知唤醒condition.signalAll(); // this.notifyAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}//	public synchronized void increment() throws InterruptedException { // 判断
//		while (number != 0) {
//			this.wait(); // A C
//		}
//		// 干活 
//		++number;
//		System.out.println(Thread.currentThread().getName() + "\t" + number); 
//		// 通知唤醒
//		this.notifyAll();
//	}
//
//	public synchronized void decrement() throws InterruptedException {
//		while (number == 0) {
//			this.wait();
//		}
//		--number;
//		System.out.println(Thread.currentThread().getName() + "\t" + number);
//		this.notifyAll();
//	}
}

注意:资源类中要用 where 作为多线程的判断,不能用 if,这样能避免线程的虚假唤醒。

题目3:8锁
示例代码如下:

package com.atguigu.thread;import java.util.concurrent.TimeUnit;/*** 8锁* 1	一部手机,正常访问,先打印苹果还是Android?答:先苹果再Android* 2	新增 TimeUnit,先打印苹果还是Android?答:先苹果再Android* 3	新增 hello 方法,先打印苹果还是hello?答:先hello再苹果* * 4	两部手机,先打印苹果还是Android?答:先Android再苹果* * 5	两个静态同步方法,一部手机,先打印苹果还是Android?答:先苹果再Android* 6	两个静态同步方法,两部手机,先打印苹果还是Android?答:先苹果再Android* * 7	1个静态同步方法,1个普通同步方法,一部手机,先打印苹果还是Android?答:先Android再苹果* 8	1个静态同步方法,1个普通同步方法,两部手机,先打印苹果还是Android?答:先Android再苹果* * 一个对象里面如果有多个 synchronized 方法,某一个时刻内,只有一个线程去调用其中的一个 synchronized 方法了,其它的线程都只能等待,* 换句话说,* 某一个时刻内,只能有唯一一个线程去访问这些 synchronized 方法。* * 锁的是当前对象 this,被锁定后,其它的线程都不能进入到当前对象的其它的 synchronized 方法* * 加个普通方法后发现和同步锁无关* * 换成两个对象后,不是同一把锁了,情况立刻变化。* * 都换成静态同步方法后,情况又变化* * 所有的非静态同步方法用的都是同一把锁--实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,--锁的是当前对象 this* 该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,* 可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,* 所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。* * 所有的静态同步方法用的也是同一把锁--类对象本身,这两把锁是两个不同的对象,--锁的是当前对象的模板 class* 所以静态同步方法与非静态同步方法之间是不会有竞态条件的。* 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,* 而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!*/
public class ThreadDemo03 {public static void main(String[] args) {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(() -> {try {phone.getIOS();} catch (Exception e) {e.printStackTrace();}}, "AA").start();new Thread(() -> {try {// phone.getAndroid();// phone.getHello();phone2.getAndroid();} catch (Exception e) {e.printStackTrace();}}, "BB").start();}
}class Phone {public static synchronized void getIOS() throws Exception {TimeUnit.SECONDS.sleep(4);System.out.println("-----getIOS");}public synchronized void getAndroid() throws Exception {System.out.println("-----getAndroid");}public void getHello() {System.out.println("-----getHello");}
}

Linux 下查询 java 进程的个数:top -H -p {pid} 或者 ps huH p {PID} | wc -l

这篇关于大数据技术之_31_Java 面试题_02_== 和 equals 有什么区别 + String 相关 + 多态 + 传值 + static 加载机制 + 线程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100776

相关文章

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

SpringBoot项目中报错The field screenShot exceeds its maximum permitted size of 1048576 bytes.的问题及解决

《SpringBoot项目中报错ThefieldscreenShotexceedsitsmaximumpermittedsizeof1048576bytes.的问题及解决》这篇文章... 目录项目场景问题描述原因分析解决方案总结项目场景javascript提示:项目相关背景:项目场景:基于Spring

Spring Boot 整合 SSE的高级实践(Server-Sent Events)

《SpringBoot整合SSE的高级实践(Server-SentEvents)》SSE(Server-SentEvents)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实... 目录1、简述2、Spring Boot 中的SSE实现2.1 添加依赖2.2 实现后端接口2.3 配置超时时

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Java中的@SneakyThrows注解用法详解

《Java中的@SneakyThrows注解用法详解》:本文主要介绍Java中的@SneakyThrows注解用法的相关资料,Lombok的@SneakyThrows注解简化了Java方法中的异常... 目录前言一、@SneakyThrows 简介1.1 什么是 Lombok?二、@SneakyThrows

Java中字符串转时间与时间转字符串的操作详解

《Java中字符串转时间与时间转字符串的操作详解》Java的java.time包提供了强大的日期和时间处理功能,通过DateTimeFormatter可以轻松地在日期时间对象和字符串之间进行转换,下面... 目录一、字符串转时间(一)使用预定义格式(二)自定义格式二、时间转字符串(一)使用预定义格式(二)自