大数据技术之_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

相关文章

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

springboot将lib和jar分离的操作方法

《springboot将lib和jar分离的操作方法》本文介绍了如何通过优化pom.xml配置来减小SpringBoot项目的jar包大小,主要通过使用spring-boot-maven-plugin... 遇到一个问题,就是每次maven package或者maven install后target中的ja

Java中八大包装类举例详解(通俗易懂)

《Java中八大包装类举例详解(通俗易懂)》:本文主要介绍Java中的包装类,包括它们的作用、特点、用途以及如何进行装箱和拆箱,包装类还提供了许多实用方法,如转换、获取基本类型值、比较和类型检测,... 目录一、包装类(Wrapper Class)1、简要介绍2、包装类特点3、包装类用途二、装箱和拆箱1、装

如何利用Java获取当天的开始和结束时间

《如何利用Java获取当天的开始和结束时间》:本文主要介绍如何使用Java8的LocalDate和LocalDateTime类获取指定日期的开始和结束时间,展示了如何通过这些类进行日期和时间的处... 目录前言1. Java日期时间API概述2. 获取当天的开始和结束时间代码解析运行结果3. 总结前言在J

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动