Java基础扩展

2021-01-17

==和equals的区别是什么

1.类型与定义
==是一个操作符,用于比较两个变量的值。对于基本数据类型,它比较的是变量存储的值是否相等;对于引用类型,它比较的是两个引用是否指向内存中的同一地址(即是否是同一个对象的引用)。
equals()是Object类中的一个方法,用于比较两个对象的内容是否相等。默认情况下,它比较的是对象的内存地址(即是否是同一个对象),但该方法可以被重写以提供自定义的比较逻辑。

2.运行速度
==:由于只是比较引用或内存地址,所以通常比equals()方法更快。
equals():由于可能需要执行更复杂的比较逻辑(尤其是在被重写的情况下),因此其运行速度可能慢于==。
3.可重写性
==:不可重写,其行为是固定的。
equals():可以被重写以提供自定义的相等性判断逻辑。

JAVA的八大基本类型

int
32位
4字节
封装类:Integer

byte
8位
1字节
封装类:Byte

double
64位
8字节
封装类:Double

float
32位
4字节
封装类:Float

long
64位
8字节
封装类:Long

char
16位
2字节
封装类:Character

short
16位
2字节
封装类:Short

boolean
8位
1字节
封装类:Boolean

JAVA中的数据类型提升

Java中的提升是指自动将低精度类型转换为高精度类型的过程。在Java中,当运算符两侧的操作数类型不同时,系统会进行自动类型转换,将低精度类型提升到高精度类型,以避免数据丢失或计算错误。

Java中的基本数据类型按照精度分为以下几类:byte、short、int、long、float和double。

其中,byte和short是最低精度的类型,double是最高精度的类型。

在进行运算时,如果操作数的类型不一致,则系统会自动将较低精度的类型提升为较高精度的类型,以保证运算结果的正确性。

例如,int a = 10; float b = 1.5f; double c = a + b; 在这个例子中,a会被自动提升为float类型,然后与b相加得到一个float类型的结果,最后再自动提升为double类型并赋值给c。

final在JAVA中有什么作用

用来修饰一个引用
如果引用为基本数据类型,则该引用为常量,该值无法修改;
如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
如果引用时类的成员变量,则必须当场赋值,否则编译会报错。

用来修饰一个方法
当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写,但是方法可以继承
用来修饰类
当用final修饰类时,该类会成为最终类,无法被继承。比如常用的String类就是最终类

byte类型127+1等于多少

byte的范围是-128~127
字节长度为8位,最左边是符号位,而127的二进制位01111111,所以执行+1操作后,01111111变为10000000。
计算机中存储负数,存的是补码的兴衰,左边第一位为符号位
那么负数的补码转换成十进制如下:
一个数如果为正,则它的原码、反码、补码相同;一个正数的补码,将其转化为十进制,可以直接转换。
已知一个负数的补码,将其转换为十进制数,步骤如下:
1.先对各位取反;
2.将其转换为十进制数;
3.加上负号,再减去1;
例如10000000,最高位是1,是负数,①对各位取反得01111111,转换为十进制就是127,加上负号得-127,再减去1得-128

String, Stringbuffer, StringBuilder的区别

1.可变性
String内部的value值是final修饰的,所以它是不可变类。所以每次修改String的值,都会产生一个新的对象。
StringBuffer和StringBuilder是可变类,字符串的变更不会产生新的对象。
2.线程安全性
String是不可变类,所以它是线程安全的。
StringBuffer是线程安全的,因为它每个操作方法都加了synchronized同步关键字。
StringBuilder不是线程安全的,所以在多线程环境下对字符串进行操作,应该使用StringBuffer,否则使用StringBuilder。
3.性能
String的性能是最低的,因为String是不可变的,这就意味着在做字符串拼接和修改的时候,需要重新创建新的对象以及分配内存。
StringBuffer因为是可变的,直接修改即可,但StringBuffer添加了重量级锁synchronized,其性能不如StringBuilder。
4.存储
String存储在字符串常量池中。
StringBuilder和StringBuffer都存储在堆内存中。

普通类和抽象类有哪些区别

实例化:
普通类可以被实例化,即可以创建这个类的对象。然而,抽象类不能被实例化。尝试实例化抽象类会导致编译错误。这是因
抽象类通常表示一种概念或行为,而不是具体的对象。
抽象方法:
抽象类可以包含抽象方法,这是没有实现的方法,只有方法的声明,没有具体的实现。普通类则不能包含抽象方法。如果
一个类包含抽象方法,那么这个类必须被声明为抽象类。
继承:
一个类可以从一个抽象类继承,但也可以从普通类继承。然而,一个抽象类只能被另一个类继承,而不能被实例化。这意味着
象类是类的基类,用于定义一些通用的属性和方法,而具体的实现则由子类来完成。
设计目的:
普通类通常用于表示具体的实体或对象,如一个具体的动物或人。而抽象类则用于表示一种概念或一组具有共同特性的
象,这些对象的具体实现由子类来完成。例如,一个”动物”类可能是一个抽象类,而”狗”和”猫”则可能是这个抽象类的具体实现。

总的来说,普通类和抽象类在实例化、包含抽象方法、继承和设计目的等方面存在明显的差异。抽象类提供了一种方式来定义一组具有共同特性的对象的概念,而具体的实现则由子类来完成。这使得抽象类在面向对象编程中非常有用,尤其是在需要创建一组具有共同行为的对象时。

什么是JAVA序列化,什么情况下需要序列化

序列化就是把内存中的对象转化为字节流,以便用于传输和储存
序列化的前提是保证通讯双方对于对象的可识别行,所以很多时候,会把对象转为通用的解析格式,如Json,Xml,等,再转化为数据流进行网络传输,从而实现跨平台和跨语言的可识别性。
序列化是通过实现serializable接口,该接口没有需要实现的方法,implement Serializable只是为了标注该对象是可被序列化的。
反序列化就是根据从文件或者网络上获取到的对象的字节流,根据字节流里面保存的对象描述信息和状态。

Java中多态的实现原理

多态机制包括静态多态(编译时多态)和动态多态(运行时多态)。
静态多态比如说重载,动态多态一般指在运行时才能确定调用哪个方法。
我们通常所说的多态一般指运行时多态,也就编译时不确定究竟调用哪个具体方法,一直等到运行时才能确定。
多态实现方式:继承extends和实现implements。
多态的核心在于子类对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。
当调用对象的某个方法时,JVM查找该对象类的方法表,以确定该方法的直接引用地址,有了地址后才真正调用该方法。

成员内部类,静态内部类,局部内部类和匿名内部类的理解,以及在项目中的应用

在Java中,内部类是定义在另一个类内部的类。根据定义的方式和作用域,内部类可以分为成员内部类、静态内部类、局部内部类和匿名内部类。

成员内部类(Member Inner Class):成员内部类是定义在另一个类内部的普通类,它可以直接访问外部类的成员(包括私有成员),并且可以使用外部类的引用。成员内部类的实例化需要先实例化外部类,然后通过外部类的实例来创建内部类的实例。

静态内部类(Static Nested Class):静态内部类是用 static 关键字修饰的内部类,它与外部类实例没有直接关联,可以直接通过外部类的类名来访问。静态内部类可以访问外部类的静态成员,但不能直接访问外部类的非静态成员,如果需要访问外部类的非静态成员,则需要通过外部类的实例来访问。

局部内部类(Local Inner Class):局部内部类是定义在方法或作用域内的类,它的作用域被限定在方法或作用域内部,外部类无法访问局部内部类,也无法通过外部类的实例来创建局部内部类的实例。局部内部类可以访问外部类的成员和方法,但是只能访问被声明为 final 的局部变量。

匿名内部类(Anonymous Inner Class):匿名内部类是没有类名的内部类,通常用于创建临时的、只使用一次的类实例。匿名内部类通常在实例化的同时定义,通常作为参数传递给方法或构造函数。

在项目中,内部类的应用可以提高代码的封装性、可读性和灵活性。例如,成员内部类可以用于实现一些逻辑上密切相关的功能,静态内部类可以用于将辅助类与主类分离,局部内部类可以用于封装复杂算法或逻辑,匿名内部类可以用于实现回调接口或创建线程。内部类的选择取决于具体的需求和设计目标。

常见的异常类

NullPointerException:空指针异常;
SQLException:数据库相关的异常;
IndexOutOfBoundsException:数组下角标越界异常;
FileNotFoundException:打开文件失败时抛出;
IOException:当发生某种IO异常时抛出;
ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;、
NoSuchMethodException:无法找到某一方法时,抛出;
ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;
NumberFormatException:当试图将字符串转换成数字时,失败了,抛出;
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。

对java.lang.Object对象中hashCode和equals方法的理解,在什么场景下需要重写这两个方法

hashCode() 和 equals() 是 java.lang.Object 类中的两个方法,它们对于 Java 中的对象比较和哈希处理非常重要。

hashCode() 方法:
hashCode() 方法返回对象的哈希码值,它是一个整数,用于快速定位对象在哈希表中的位置。默认情况下,hashCode() 方法返回对象的内存地址的哈希码。但是,如果在类中重写了 hashCode() 方法,则应根据对象的内容来计算哈希码,保证相等对象具有相等的哈希码。重写 hashCode() 方法的主要原因是为了保证对象在放入哈希集合(如 HashMap、HashSet)时,能够正确地根据对象的内容进行查找和比较。


equals() 方法:
equals() 方法用于比较两个对象是否相等。默认情况下,equals() 方法比较的是对象的引用(即内存地址),即只有当两个对象引用同一个内存地址时才返回true。重写 equals() 方法的主要目的是根据对象的内容来定义相等性。因此,在重写 equals() 方法时,通常需要比较对象的属性值而不是对象的引用。equals() 方法的重写通常与 hashCode() 方法的重写一起进行,以保证相等的对象具有相等的哈希码,并且相等的对象通过 equals() 方法比较时返回true。


需要重写 hashCode() 和 equals() 方法的情况包括:
在自定义类中,当需要将对象放入哈希集合(如 HashMap、HashSet)中,并且希望根据对象的内容来比较和查找时,应该重写这两个方法。

如果两个对象的内容相同,但是它们的哈希码不同,那么它们将被认为是不相等的,即使它们的内容相同。因此,为了确保哈希集合正常工作,应该保证相等的对象具有相等的哈希码。

为什么相同的hashCode的两个对象有可能不相等

相同的哈希码(hashCode)并不保证两个对象一定相等,这是因为哈希码是根据对象的内容计算的,而不是根据对象的引用。
因此,不同的对象即使具有相同的哈希码,它们的内容也可能不相等。
哈希码冲突是不可避免的,因为哈希码的范围是有限的,而对象的数量是无限的。
这意味着不同的对象可能会映射到相同的哈希码。Java的哈希函数通常会尽量减少哈希冲突的概率,但无法完全避免。
在哈希集合(如HashMap、HashSet)中,当两个对象具有相同的哈希码时,HashMap会使用equals()方法来进一步比较这两个对象是否相等。如果equals()方法返回true,则HashMap会将它们视为相等的键;
否则,它们被视为不相等的键。因此,重写equals()方法是确保正确比较对象内容的关键。
hashCode()方法的主要作用是为了提高哈希集合的性能,使得具有相同内容的对象具有相同的哈希码,从而减少哈希冲突的概率。

深拷贝和浅拷贝的区别

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在对象复制过程中的两种不同方式,它们之间的区别主要在于拷贝的深度和拷贝的内容。

浅拷贝(Shallow Copy):
浅拷贝是指仅复制对象本身以及对象中包含的基本数据类型的成员变量,而不会复制对象中的引用类型的成员变量。在浅拷贝中,原始对象和拷贝对象共享同一份引用类型的成员变量,因此修改拷贝对象中的引用类型成员变量可能会影响到原始对象中对应的成员变量。

深拷贝(Deep Copy):
深拷贝是指复制对象本身以及对象中包含的所有成员变量,包括基本数据类型和引用类型。在深拷贝中,原始对象和拷贝对象拥有独立的引用类型成员变量,修改拷贝对象中的引用类型成员变量不会影响到原始对象中对应的成员变量。总的来说,浅拷贝只复制了对象的表层结构,而深拷贝则复制了整个对象结构。因此,深拷贝的复制过程更加复杂和耗时,但是在某些情况下(如对象包含引用类型的成员变量),深拷贝是必要的,以确保不同对象之间的独立性。

JAVA中有哪几种方式创建线程执行任务

1.继承Thread类
重写了run()方法而不是start()方法,占用了继承名额

1
2
3
4
5
6
7
8
9
10
11
public class Thread extends Thread{
public static void main(String args[]){
ThreadObj thread = new ThreadObj;
thread.start();
}

@Override
public void run(){
System.out.println("启动线程");
}
}

2.实现Runnable对象

1
2
3
4
5
6
7
8
9
10
public class ThreadClass implements Runnable{
public static void main(String args[]){
ThreadObj thread = new ThreadObj(new ThreadClass());
thread.start();
}

public void run(){
System.out.println("启动线程");
}
}
1
2
3
4
5
6
7
8
9
10
public class ThreadClass implements Runnable{
public static void main(String args[]){
ThreadObj thread = new ThreadObj(new ThreadClass(){
public void run(){
System.out.println("启动线程");
}
})
thread.start();
}
}
1
2
3
4
5
public class ThreadClass implements Runnable{
public static void main(String args[]){
ThreadObj thread = new Thread( () -> Sstem.out.println("启动线程");
}
}