下列说法正确的有()
A. class中的constructor不可省略
B. constructor必须与class同名,但方法不能与class同名
C. constructor在一个对象被new时执行
D. 一个class只能定义一个constructor
正确答案是C。对于下列关于构造函数(constructor)的说法,我们可以逐一进行分析:
- class中的constructor不可省略:
这个说法是不正确。在Java中,如果你没有为类显式地定义一个构造函数,编译器会自动为你提供一个默认的无参构造函数。但是,如果你已经定义了至少一个构造函数(无论是有参还是无参),那么编译器就不会再为你提供默认的无参构造函数了。因此,从某种角度上说,构造函数在某些情况下是可以“省略”的,但更准确的说法是,如果类中没有显式定义任何构造函数,编译器会提供一个默认的。
- constructor必须与class同名,但方法不能与class同名
这个说法的前半部分是正确的,构造函数必须与类同名。但是后半部分的说法是错误的,因为虽然你可以定义一个与类名同名的方法(比如public void 类名() {}
),但这将是一个普通的方法,而不是构造函数。构造函数的特殊之处在于它用于初始化对象,并且在创建对象时自动调用,而普通方法则需要显式调用。
class Base {
// 构造函数
Base() {
System.out.print("Base构造");
}
// 方法
void Base(){
System.out.println("方法");
}
}
- constructor在一个对象被new时执行:
这个说法是正确的。当使用new
关键字创建对象时,会调用该类的构造函数来初始化对象。
- 一个class只能定义一个constructor // 重载:
这个说法是错误的。一个类可以定义多个构造函数,这些构造函数通过参数列表的不同(参数类型、参数个数或参数的顺序)来实现重载。这样,就可以根据不同的需求使用不同的构造函数来创建对象。
public class end {
public static void main( String[] args ) {
int i = 0;
for (i++; i++ < 10; i++);
System.out.println(++i);
}
}
运行结果:13
下列哪些代码符合java规范:
A. public static void main(){}
B. public method(){ private Integer num; }
C. public method(){ int num; System.out.println(num); } //局部变量必须赋初值
D. private $5person=5;
- 对于A选项,可以这种写法相当于以main命名的普通方法,
main方法
的写法为:
public static void main( String[] args ){}
- 对于B选项,局部变量必须赋初值
- 对于D选项,定义一个变量时没有写数据类型
请看下面的程序段,哪一个选项是正确的( )。
public class Test{
long a[]=new long[10];
public static void main(String args[]){
System.out.println(a[6]);
}
}
A. 不输出任何内容
B. 输出0
C. 当编译时有错误出现
D. 当运行时有错误出现
答案是当编译时有错误出现,以下是详细的分析:main
方法是一个静态方法,这意味着它可以在类被加载后,不需要创建类的实例就能被调用。而在 main
方法中,试图访问类中的实例变量 a
(a
是一个数组,属于实例变量,因为它是直接定义在类内部,没有使用 static
修饰)。
静态方法只能直接访问类中的静态成员(静态变量、静态方法等),不能直接访问非静态的实例成员。因为非静态成员是和类的具体实例相关联的,在没有创建实例时,这些非静态成员并没有在内存中被分配相应的空间,其具体的值等情况都是不确定的。
在这里,main
方法直接去访问 a
数组并试图输出其中的元素,违反了静态方法的访问规则,所以在编译阶段,编译器就能检测到这个错误,会提示类似“无法从静态上下文中引用非静态 变量 a
”这样的编译错误,导致代码无法通过编译。
补充: 类加载的时候,先加载静态成员和静态代码块,且非静态的成员不能放进静态代码块中。类加载时的动作:
- 静态成员和静态初始化块
- 实例成员和实例初始化块
- 构造方法
Java 规定在子类构造方法中,如果要显式地调用父类的构造方法,那么这个调用必须放在子类构造方法的第一条语句的位置,这可以通过使用 super 关键字来实现(如果调用父类的无参构造方法,在没有显式写 super() 的情况下,编译器会自动添加;如果要调用父类的有参构造方法,则需要显式地写成 super(参数列表) 的形式)。
class Parent {
private int num;
public Parent(int num) {
this.num = num;
}
}
class Child extends Parent {
private String name;
public Child(int num, String name) {
super(num); // 必须放在构造方法第一条语句位置,调用父类有参构造方法
this.name = name;
}
}
以下类型为final类型的为()
A. HashMap
B. StringBuffer //public final class
C. String
D. Hashtable
final
关键字的作用:
- final修饰变量就变为常量
- final修饰方法这个方法就不能重写
- final修饰类这个类就不能被继承。
这题的答案是 String
和 StringBuffer
,以下是具体分析:
String
类
在Java中,String
类被声明为 final
类,意味着它不能被继承,这是Java语言设计层面的一种决策,目的是为了保证字符串对象在使用过程中的不可变特性以及一些安全性、性能等方面的考虑。例如:
String str = "Hello";
// 尝试去继承String类,以下代码会编译失败
class MyString extends String {
// 编译错误,因为String是final类,不能被继承
}
由于 String
类是 final
的,其内部的一些实现细节(比如字符串的存储方式、各种字符串操作方法的逻辑等)可以保证始终按照预定的、稳定的方式来运行,外界无法通过继承去改变它的行为或者破坏它的不可变性质(一旦创建了 String
对象,其内容是不可修改的,对字符串的修改操作实际上是创建了新的字符串对象)。
StringBuffer
类
StringBuffer
类被声明为 public final class
,同样也是 final
类型,它不能被继承。StringBuffer
主要用于在需要对字符序列进行可变操作(比如频繁的字符串拼接等情况)时使用,虽然它自身的内容可以修改,但从类的可扩展性角度,Java将其设计为 final
类,避免了因为继承可能带来的一些对其内部可变逻辑的不当修改或者破坏等情况。例如:
// 以下尝试继承StringBuffer会编译失败
class MyStringBuffer extends StringBuffer {
// 编译错误,因为StringBuffer是final类,不能被继承
}
HashMap
和Hashtable
类
HashMap
和 Hashtable
这两个类在Java中都不是 final
类型的类,它们都是实现了 Map
接口用于存储键值对数据的数据结构类,开发人员可以根据实际需求,通过继承它们来扩展一些特定的功能或者对其进行一些自定义的改造等操作,例如可以创建自定义的类继承自 HashMap
然后重写部分方法来满足特殊的业务逻辑需求(当然要遵循相应的重写规则等要求)。
关于下面的一段代码,以下哪些说法是正确的:
public static void main(String[] args) {
String a = new String("myString");//这里先new了一个"myString"字符串放到了常量池里,又new了一个新的对象"myString"。
String b = "myString";// b使用常量池里的
String c = "my" + "String";//c也一样
String d = c;// d也一样.所以b == c == d
System.out.print(a == b);
System.out.print(a == c);
System.out.print(b == c);
System.out.print(b == d);
}
A. System.out.print(a == b)打印出来的是false
B. System.out.print(a == c)打印出来的是true
C. System.out.print(b == c)打印出来的是false
D. System.out.print(b == d)打印出来的是true
System.out.print(a == b)
分析:
在代码中,a
是通过 new String("myString")
方式创建的字符串对象。这种方式会首先在字符串常量池中查看是否已经存在 "myString"
这个字符串常量,如果不存在就先在常量池中创建一个,然后再在堆上创建一个新的 String
对象来存储这个字符串内容,此时变量 a
指向的是堆上这个新创建的 String
对象。
而 b
是通过 String b = "myString"
方式定义的,这种形式会直接去字符串常量池中查找 "myString"
,如果存在就直接将变量 b
指向该常量池中的这个字符串对象(这里由于之前创建 a
时已经在常量池中有了 "myString"
,所以 b
直接指向常量池中的这个字符串)。
所以,a
和 b
虽然存储的字符串内容相同,但它们指向的是不同位置的对象(一个是堆上创建的,一个是指向常量池中的),使用 ==
比较时,比较的是两个对象的引用地址是否相同,因此 a == b
的结果为 false
。
System.out.print(a == c)
分析:
c
是通过 String c = "my" + "String"
方式定义的。在Java编译阶段,对于这种由常量字符串拼接而成的表达式,编译器会进行优化,直接将其合并为一个常量字符串 "myString"
,然后 c
会指向字符串常量池中的 "myString"
这个字符串对象。
而前面分析过 a
指向的是堆上通过 new
创建的字符串对象,所以 a
和 c
指向的不是同一个对象,a == c
的结果为 false
,并非 true
。
System.out.print(b == c)
分析:
如前面所述,b
是直接指向字符串常量池中的 "myString"
字符串对象,c
经过编译优化后也指向字符串常量池中的 "myString"
字符串对象,所以 b
和 c
指向的是同一个对象,b == c
的结果为 true
,并非 false
。
System.out.print(b == d)
分析:
d
是通过 String d = c
定义的,由于 c
指向的是常量池中的 "myString"
,赋值操作会使得 d
也指向这个相同的常量池中的对象,而 b
同样指向这个常量池中的 "myString"
对象,所以 b
和 d
指向的是同一个对象,b == d
的结果为 true
。
综上,正确的说法是:
System.out.print(a == b)
打印出来的是false
。System.out.print(a == c)
打印出来的是false
。System.out.print(b == c)
打印出来的是true
。System.out.print(b == d)
打印出来的是true
。
关于Java中的数组,下面的一些描述,哪些描述是准确的:( )
A. 数组是一个对象,不同类型的数组具有不同的类
B. 数组长度是可以动态调整的
C. 数组是一个连续的存储结构
D. 一个固定长度的数组可类似这样定义: int array[100]
E. 两个数组用equals方法比较时,会逐个便利其中的元素,对每个元素进行比较
F. 可以有二维数组,且可以有多维数组,都是在Java中合法的
以下是对每个描述的详细分析:
- “数组是一个对象,不同类型的数组具有不同的类”
在Java中,数组确实是对象,它是一种特殊的对象类型。并且不同类型的数组在Java的类层次结构中属于不同的类,例如 int[]
数组是 [I
类型(这是Java内部表示整型数组的一种类型标识),String[]
数组是 [Ljava.lang.String;
类型等,它们在运行时系统中被当作不同的类来对待,有着各自对应的类信息,所以该描述准确。
- “数组长度是可以动态调整的”
一旦数组在Java中被创建,其长度就是固定的,不能直接动态地去改变它的长度。
与之不同的是,像 ArrayList
这类集合类,它内部封装了动态调整大小的机制,可以方便地添加或删除元素,使得其容量能根据元素数量动态变化,但数组本身不具备这样的特性,所以该描述不准确。
- “数组是一个连续的存储结构”
在Java的内存模型中,数组元素在内存中是按照连续的顺序存储的,这样便于通过索引快速地访问数组中的各个元素,只需根据数组的起始地址以及元素类型的大小和索引值,就能快速定位到对应的元素位置,所以该描述准确。
- “一个固定长度的数组可类似这样定义:
int array[100]
”
在Java中,数组的定义方式是 数据类型[] 数组名 = new 数据类型[长度];
或者 数据类型[] 数组名 = {元素列表};
等形式.
像 int array[100]
这种是C或C++语言中定义数组的语法格式,不符合Java中数组的定义语法规则,所以该描述不准确。
- “两个数组用
equals
方法比较时,会逐个便利其中的元素,对每个元素进行比较”
对于数组对象,默认情况下(也就是没有重写 equals
方法时,数组类本身自带的 equals
方法),比较的是两个数组对象的引用是否相等(对象地址是否相等),也就是判断它们是否指向同一个数组对象,而不是逐个遍历元素去比较内容。
当然,如果自定义类继承自数组类并且重写了 equals
方法,那就可以实现按元素逐个比较的逻辑,但就Java数组本身的默认行为而言,不是这样比较的,所以该描述不准确。
- “可以有二维数组,且可以有多维数组,都是在Java中合法的”
在Java中,不仅支持二维数组(例如 int[][]
表示二维整型数组),还支持更高维度的多维数组(如 int[][][]
等形式),通过嵌套数组的方式来构建多维数组结构,在语法和使用上都是合法的,所以该描述准确。
下列说法正确的是()
A. JAVA程序的main方法必须写在类里面
B. JAVA程序中可以有多个名字为main方法
C. JAVA程序中类名必须与文件名一样 //应该是要和public类名一致
D. JAVA程序的main方法中,如果只有一条语句,可以不用{}(大括号)括起来
正确答案是A。以下是对每个说法的详细分析:
- “JAVA程序的main方法必须写在类里面”
在Java中,main
方法是Java程序的入口点,它规定必须定义在类当中。整个Java程序的执行是从 main
方法开始启动的,并且它需要遵循 public static void main(String[] args)
这样特定的方法签名形式(当然,参数名 args
可以自定义,但类型等必须符合要求),所以该说法正确。
- “JAVA程序中可以有多个名字为main方法”
一个Java程序中,作为程序启动入口的 main
方法(也就是符合 public static void main(String[] args)
签名的方法)在整个程序范围内只能有一个。因为Java虚拟机(JVM)在启动Java程序时,会去查找并执行这个唯一的入口 main
方法,如果存在多个符合该签名的 main
方法,JVM就无法确定到底该执行哪一个来启动程序,会导致编译错误,所以该说法错误。
- “JAVA程序中类名必须与文件名一样”
准确来说,在Java中要求的是如果一个类被声明为 public
类,那么这个类的类名必须和文件名(不包括 .java
扩展名)一致,一个 .java
文件中可以包含多个类,但最多只能有一个 public
类。如果没有 public
类,文件名可以和其中任意一个类名不同,不过按照良好的编程规范和便于代码管理、编译等角度,通常也会尽量让文件名和主要类名相关联,所以该说法错误。
- “JAVA程序的main方法中,如果只有一条语句,可以不用 {}(大括号)括起来”
在Java语法规范中,方法体无论包含几条语句,都应该使用大括号 {}
括起来,这是为了保证代码的清晰性、可读性以及符合语法规则要求。即使 main
方法中暂时只有一条语句,也需要用大括号括起来,否则可能会引发一些语法错误或者不符合代码规范的情况,所以该说法错误。
Java的集合框架中重要的接口java.util.Collection定义了许多方法。
选项中哪个方法是Collection接口所定义的( )多选
A. int size()
B. boolean containsAll(Collection c)
C. compareTo(Object obj)
D. boolean remove(Object obj)
Collection接口是所有集合类的顶级接口之一(另一个重要的顶级接口是Map,用于存储键值对形式的数据),它为各种具体的集合实现类(如ArrayList、LinkedList、HashSet等)提供了统一的操作规范,使得开发者可以用一致的方式来处理不同类型的集合,而不用关心具体的集合实现细节。
常用方法
(1)添加元素相关方法
boolean add(E e)
:
用于向集合中添加一个指定的元素。这里的E
表示集合中元素的类型,不同的具体集合实现类对于添加元素的规则可能有所不同,比如ArrayList
基本可以直接添加,而HashSet
会基于元素的哈希值和equals
方法来判断是否重复添加。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionAddExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
}
}
上述代码创建了一个ArrayList
实现的Collection
,并向其中添加了两个字符串元素。
boolean addAll(Collection<? extends E> c)
:
用于将指定集合c
中的所有元素添加到当前集合中。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionAddAllExample {
public static void main(String[] args) {
Collection<String> collection1 = new ArrayList<>();
collection1.add("apple");
Collection<String> collection2 = new ArrayList<>();
collection2.add("banana");
collection2.add("cherry");
collection1.addAll(collection2);
}
}
这里将collection2
中的元素都添加到了collection1
中,操作完成后collection1
包含"apple"
、"banana"
、"cherry"
三个元素。
(2)删除元素相关方法
boolean remove(Object o)
:
尝试从集合中移除指定的对象o
,如果集合中存在该对象并且成功移除,则返回true
,否则返回false
。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionRemoveExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
boolean result = collection.remove("apple");
System.out.println(result);
}
}
代码中尝试移除"apple"
元素,若移除成功则打印true
。
boolean removeAll(Collection<?> c)
:
从当前集合中移除指定集合c
中包含的所有元素,返回值表示当前集合是否发生了改变(即是否有元素被移除)。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionRemoveAllExample {
public static void main(String[] args) {
Collection<String> collection1 = new ArrayList<>();
collection1.add("apple");
collection1.add("banana");
Collection<String> collection2 = new ArrayList<>();
collection2.add("apple");
boolean result = collection1.removeAll(collection2);
System.out.println(result);
}
}
此例中会从collection1
中移除collection2
包含的元素(这里是"apple"
),若有元素被移除则打印true
。
(3)查询元素相关方法
int size()
:
返回集合中元素的个数,这在很多场景下都很有用,比如遍历集合前想知道集合规模等。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionSizeExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
System.out.println(collection.size());
}
}
代码会输出2
,表示当前集合包含两个元素。
boolean contains(Object o)
:
用于判断集合中是否包含指定的对象o
,返回true
表示包含,否则返回false
。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionContainsExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
boolean result = collection.contains("apple");
System.out.println(result);
}
}
会输出true
,因为集合中存在"apple"
元素。
boolean containsAll(Collection<?> c)
:
判断当前集合是否包含指定集合c
中的所有元素,常用于比较两个集合之间的包含关系。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionContainsAllExample {
public static void main(String[] args) {
Collection<String> collection1 = new ArrayList<>();
collection1.add("apple");
collection1.add("banana");
Collection<String> collection2 = new ArrayList<>();
collection2.add("apple");
boolean result = collection1.containsAll(collection2);
System.out.println(result);
}
}
这里会输出true
,因为collection1
包含了collection2
中的所有元素。
(4)遍历相关方法(间接通过迭代器等方式)
虽然Collection
接口本身没有定义直接用于遍历的像for
循环那样的方法,但可以通过获取迭代器(Iterator
)来实现遍历。例如:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionIteratorExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
}
}
上述代码先获取集合的迭代器,然后通过hasNext
判断是否还有下一个元素,再通过next
方法逐个获取并打印元素,实现了集合的遍历。
(5)集合转换相关方法
Object[] toArray()
:
将集合转换为一个对象数组,返回的数组元素顺序和集合中元素的顺序一致(对于有序集合而言)。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionToArrayExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
Object[] array = collection.toArray();
for (Object element : array) {
System.out.println((String) element);
}
}
}
代码将集合转换为数组后进行遍历打印元素。
<T> T[] toArray(T[] a)
:
这是一个泛型方法,它可以将集合元素存储到指定类型的数组a
中,如果a
长度足够则直接存储进去,否则会创建一个新的合适长度的同类型数组来存储,返回值就是存储了集合元素的数组。例如:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionToArrayGenericExample {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.add("banana");
String[] array = new String[3];
String[] resultArray = collection.toArray(array);
for (String element : resultArray) {
System.out.println(element);
}
}
}
这里创建了一个长度为3
的String
数组,将集合元素存储到该数组中(若数组长度不足会自动创建新的合适长度数组),然后遍历打印元素。
主要实现类及特点
(1)List
接口及实现类(如ArrayList
、LinkedList
)
ArrayList
:
基于数组实现,内部维护了一个可变大小的数组,它可以动态扩容。访问元素速度快,通过索引可以直接获取元素(时间复杂度为O(1)
),但在插入和删除元素(特别是在中间位置操作时)效率相对低些,因为可能涉及到数组元素的移动(插入和删除的平均时间复杂度为O(n)
)。常用于需要频繁查询元素的场景,例如存储数据库查询结果等。
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("apple");
arrayList.add("banana");
System.out.println(arrayList.get(0));
}
}
上述代码创建ArrayList
并添加元素,然后通过索引0
获取并打印第一个元素。
LinkedList
:
基于双向链表实现,插入和删除元素速度较快(在链表头部或尾部操作时间复杂度为O(1)
,在中间位置操作平均时间复杂度为O(n)
),但访问元素相对慢些,因为需要从链表头开始逐个遍历查找(通过索引访问元素的时间复杂度为O(n)
)。适用于需要频繁插入和删除元素的场景,比如实现队列、栈等数据结构时。
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("apple");
linkedList.add("banana");
linkedList.addFirst("cherry");
System.out.println(linkedList.get(0));
}
}
代码创建LinkedList
并添加元素,还通过addFirst
方法在头部添加元素,最后获取并打印第一个元素。
(2)Set
接口及实现类(如HashSet
、TreeSet
)
HashSet
:
基于哈希表实现,它不允许集合中存在重复元素,添加、删除、查询元素的平均时间复杂度都是O(1)
,效率较高。它通过元素的哈希值来判断元素是否重复以及进行存储位置的确定等操作。常用于需要快速查找元素以及保证元素唯一性的场景,例如存储用户的唯一标识等。
import java.util.HashSet;
public class HashSetExample {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("apple");
hashSet.add("apple");
System.out.println(hashSet.size());
}
}
代码中虽然添加了两次"apple"
,但由于HashSet
不允许重复,最终集合大小为1
。
TreeSet
:
基于红黑树实现,它也不允许重复元素,并且元素会按照自然顺序(对于实现了Comparable
接口的元素类型)或者指定的比较器顺序进行排序,添加、删除、查询元素的时间复杂度通常为O(log n)
。常用于需要对元素进行排序后存储和查询的场景,比如存储有序的学生成绩等。
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(3);
treeSet.add(7);
System.out.println(treeSet.first());
}
}
代码创建TreeSet
并添加整数元素,然后通过first
方法获取并打印集合中的最小元素,输出会是3
,因为元素按照从小到大顺序存储。
适用场景总结
- 需要存储一组对象,并进行添加、删除、查询、遍历等常规操作时,可以选择合适的
Collection
实现类来满足需求。 - 如果注重快速查询元素,对插入删除中间元素操作频率不高,
ArrayList
是不错的选择;如果经常要在集合头部或尾部插入删除元素,LinkedList
更合适;需要保证元素唯一性时,HashSet
或TreeSet
可以按需选用,同时若还要求元素有序,那就选择TreeSet
。
8、关于ThreadLocal类 以下说法正确的是
A. ThreadLocal实现了Runnable接口
B. ThreadLocal重要作用在于多线程间的数据共享
C. ThreadLocal是采用哈希表的方式来为每个线程都提供一个变量的副本
D. ThreadLocal保证各个线程间数据安全,每个线程的数据不会被另外线程访问和破坏
Thread
类是 Java 中用于代表线程的类,它用于创建和管理线程的执行等相关操作。
Thread thread = new Thread();
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
System.out.println(thread instanceof Thread); // 输出true
System.out.println(threadLocal instanceof Thread); // 输出false
Runnable
接口是定义了线程执行逻辑的接口,实现了该接口的类需要重写run方法来指定线程运行时要执行的任务。
class MyRunnable implements Runnable {
@Override
public void run() {
// 这里定义线程执行逻辑
}
}
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
System.out.println(threadLocal instanceof Runnable); // 输出false
ThreadLocal
的关键作用是在多线程环境中隔离数据,让每个线程都拥有属于自己的变量副本,避免多个线程之间对共享变量的并发访问冲突等问题。例如,假设有一个多线程的 Web 应用场景,每个线程处理不同用户的请求,使用ThreadLocal
可以为每个线程单独存储像用户登录信息等数据,而不是共享这些数据给所有线程,代码示例:
ThreadLocal<String> threadLocal = new ThreadLocal<>();
Thread thread1 = new Thread(() -> {
threadLocal.set("user1 info");
System.out.println(threadLocal.get());
});
Thread thread2 = new Thread(() -> {
threadLocal.set("user2 info");
System.out.println(threadLocal.get());
});
thread1.start();
thread2.start();
以下程序执行后,错误的结果是()
public class end {
private String name = "abc";
public static void main(String[] args) {
end test = new end();
end testB = new end();
String result = test.equals(testB) + ",";
result += test.name.equals(testB.name) + ",";
result += test.name == testB.name;
System.out.println(result);
}
}
A. true,true,true
B. true,false,false
C. false,true,false
D. false,true,true
关于
test.equals(testB)
- 在Java中,默认情况下,如果一个类没有重写
equals
方法,它会继承自Object
类的equals
方法实现。Object
类中equals
方法的默认行为是比较两个对象的内存地址是否相同(也就是是否为同一个对象)。 - 在给定的代码中,
test
和testB
是通过new
关键字分别创建的两个不同的Test
类实例,它们在内存中有不同的存储位置,所以test.equals(testB)
会返回false
。
关于
test.name.equals(testB.name)
- 这里比较的是两个
Test
类对象中的name
属性。两个对象的name
属性都被初始化为字符串"abc"
。 - 在Java中,字符串是一种特殊的对象,字符串常量会被存储在字符串常量池中。当使用
==
比较字符串时,比较的是它们在内存中的地址(也就是是否是同一个字符串对象),而使用equals
方法比较字符串时,比较的是字符串的内容是否一致。 - 对于直接使用双引号声明的字符串常量,只要内容相同,它们在字符串常量池中对应的是同一个对象(Java会进行这样的优化,避免重复创建相同内容的字符串对象)。所以
test.name.equals(testB.name)
会返回true
,因为它们的内容都是"abc"
。
关于
test.name == testB.name
- 如前面所述,因为
test
和testB
对象中的name
属性都被初始化为字符串常量"abc"
,而相同内容的字符串常量在Java中是同一个对象(存储在字符串常量池中),所以test.name == testB.name
会返回true
。
综上所述,除了false,true,true
之外的结果都是错误的。
下面的Java赋值语句哪些是有错误的 () MULTI CHOICE
A. int i =1000;
B. float f = 45.0;
C. char s = ‘\u0639’;
D. Object o = ‘f’;
E. String s = “hello,world\0”;
F. Double d = 100;
答案:BF
选项B
float f = 45.0;
:
这是一个错误的赋值语句。在Java中,默认的浮点型字面量(像45.0
这种形式)属于double
类型。如果要将其赋值给float
类型的变量,需要进行类型转换或者在字面量后面添加f
或F
来明确表示它是float
类型的数值。正确的写法可以是float f = 45.0f;
或者float f = (float)45.0;
,否则会出现编译错误,因为double
类型的值不能直接赋给float
类型变量。
选项C
char s = '\u0639';
这是一个正确的赋值语句。在Java中,char
类型可以使用Unicode编码来表示字符,'\u0639'
是一个合法的Unicode编码表示的字符,可以赋值给char
类型的变量s
,不会有语法问题。
选项D
Object o = 'f';
:
这是一个正确的赋值语句。在Java中,Object
是所有类的根类,char
类型的值(这里'f'
是一个字符)可以自动向上转型为Object
类型,所以可以将'f'
赋值给Object
类型的变量o
,符合Java的类型转换规则。
选项E
String s = "hello,world\0";
:
Java中的字符串可以包含任何Unicode字符,包括空字符(null character,即 \0
或 ‘\u0000’)。当您在字符串字面量中包含 \0
时,它会被视为一个普通的字符,其Unicode值为0。这个字符会被存储在字符串的字符数组中,并且字符串的长度会包括这个字符。
在Java中使用 \0
作为字符串字面量的一部分是合法的,不会导致字符串被截断。字符串的结束是由其长度属性决定的,而不是由 \0
字符决定的。在Java中,字符串字面量不需要(也不应该)包含显式的结束符 \0
来表示字符串的结束,因为Java的字符串处理机制已经内置了对字符串长度的管理。
选项F
Double d = 100;
:
这是一个错误的赋值语句。100
是一个int
类型的字面量,而Double
是java.lang
包下的包装类,用于表示double
类型的数据。在Java中,基本类型int
不能直接赋值给包装类Double
类型的变量,需要进行类型转换,比如写成Double d = 100.0;
(这里100.0
是double
类型的字面量)或者Double d = (Double)100.0;
(先将int
类型转换为double
类型再赋值给Double
包装类变量)等正确的方式才行,否则会出现编译错误。