2

java常见问题

 2 years ago
source link: https://blog.51cto.com/u_15558033/5606901
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

java常见问题

推荐 原创

苦糖? 2022-08-22 09:20:15 ©著作权

文章标签 链表 常量池 数组 文章分类 Java 编程语言 yyds干货盘点 阅读数239

1 值传递与引用传递

1.1 值传递

是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。(因为值传递的时候,实际上是将实参的值复制一份给形参。)

public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}

num1 = 10

num2 = 20

1.2 引用传递

传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到实际的对象上。(因为引用传递的时候,实际上是将实参的地址值复制一份给形参。

public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
//将数组的第一个元素变为0
array[0] = 0;
}
1.3 一些特殊的例子
1.3.1 StringBuffer类型传递
public void method1() {
StringBuffer str = new StringBuffer("1234");
System.out.println(str);
change1(str);
System.out.println(str);
}

public static void change1(StringBuffer str) {
str = new StringBuffer("abc");

//str.append("欢迎大家关注");//输出:1234欢迎大家关注
//str.insert(3, "(编程)");//输出:123(编程)4
}

分析:因为在 change1方法内部我们是新建了一个StringBuffer对象,所以 str指向了另外一个地址,相应的操作也同样是指向另外的地址的。

public class Demo {
public static void main(String[] args) {
Person p = new Person("张三");
change(p);
System.out.println(p.name);
}

public static void change(Person p) {
Person person = new Person("李四");
p = person;
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
}

输出 张三 因为change方法中重新创建了一个 Person对象。

1.3.2 String类型传递
public void method2() {
String str = new String("1234");
System.out.println(str);
change2(str);
System.out.println(str);
}

public static void change2(String str) {
// str="abc"; //输出:1234
str = new String("abc"); //输出:1234
}

2 ==与equals

基本数据类型比较的是值,引用数据类型比较的是内存地址

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

情况1:类没有重写equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。

情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。

public static void change2(String str) {
// str="abc"; //输出:1234
str = new String("abc"); //输出:1234
}public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}

当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。

3 hashCode()与equals()

hashSet比较重复

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。

如果两个对象相等,则hashcode一定也是相同的

两个对象相等,对两个对象分别调用equals方法都返回true

两个对象有相同的hashcode值,它们也不一定是相等的

4. Java文件运行过程

4.1 处理过程

Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。

4.2 对象的创建过程
java常见问题_链表
4.3 内存分析
java常见问题_常量池_02

JDK1.7及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池

Java 基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。

两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。

5 Java集合

java常见问题_数组_03

List接口下:

Arraylist :底层使用的是Object数组;

LinkedList :底层使用的是双向循环链表数据结构;

从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

java常见问题_链表_04

Vector:底层使用的是Object数组;所有方法都是同步的

Map接口下:

HashMap : JDK1.8 之前 HashMap 由 数组+链表 组成的(“链表散列” 即数组和链表的结合体),数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(HashMap 采用 “拉链法也就是链地址法” 解决冲突)

jdk1.8 数组+链表/红黑二叉树

TreeMap: 红黑二叉树

红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。

HashTable:数组+链表组成的

HashMap和HashTable的区别

线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);

效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;

对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。

初始容量大小和每次扩充容量大小的不同 : ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂次方作为哈希表的大小,后面会介绍到为什么是2的幂次方。

底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

Set接口:

TreeSet :红黑二叉树

HashSet:基于 HashMap 实现的,底层采用 HashMap 来保存元素


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK