目录
01-equals方法
理解解析 :
02-线程效率与安全
理解解析:
03-toString的默认实现
理解解析:
04-list的remove与removeAll
理解解析:
05-装箱、拆箱
理解解析:
06-Integer与int
理解解析:
07-泛型
01
理解解析:
02
理解解析:
03
理解解析:
08-Comparable与Comparator
理解解析:
09-hashCode
理解解析:
10-static代码块与代码块
理解解析:
equals方法
错选为:
正确结果为:
理解解析 :
1、== 用于比较两个对象,且比较的是两个对象内存中的地址。
stu1为字符串“Tom”,stu2是再次new出来的字符串,明显地址值不同,一定会先输出false。
2、equals属于Object类,默认情况下的实现如下图,就是 ==
因而此处方法重写了 。
3、Student stu = (Student) obj 这里用到了向下造型 如图:
对于子类拥有而父类没有的变量与方法,则需要将父类强制类型转换为子类类型,则可以引用子类中的变量和方法,题中obj可以引用Student类中的变量name。
4、接下来便是stu.name 与 this.name判断是否相等 ,而两者都是字符串String,因而需比较的是字符串字面量 ,因而需要用equals方法而不是==,字符串的比较不能用==比较,因为比较的不是地址。
02-线程效率与安全
正确选择为:B
理解解析:
1、StringBuilder:效率高
StringBuffer:线程安全 而题中应有StringBuffer因而安全
2、替换后只是效率提高,结果没有影响
3、已知String字符串本身是不变的,因而 return "<" + this.name + ">" 字符串的拼接会使每一次拼接都new出一个新的字符串,并没有提高程序性能。
03-toString的默认实现
当时不明所以,正确答案为:A、C
理解解析:
1、toString方法属于Object类,源码中默认形式如下:
其省略了this ,具体为:
return this.getClass().getName() + "@" + Integer.toHexString(hashCode());
2、getClass():获得字节码类型 getName() : 类的全限定类名(带包结构),对于Integer.toHexString(hashCode())指的就是调用hashCode方法。
3、js是对象,题中最后两行本质一样,输出对象就是会默认调用对象的toString 方法。
04-list的remove与removeAll
正确选择为:A
理解解析:
1、c选项只可删除一个java ,而对于removeAll的使用如下图,括号内需得是集合:
2、对于B选项,for循环中i由0至list的长度,但对于每一次的remove,list的长度会随之减少,会致使遍历不全,A选项中由末尾至开头则list长度的减少不会有影响。
05-装箱、拆箱
正确选择为:false true
理解解析:
public class Demo23 {
public Integer startingI;
public void methodA() {
startingI = new Integer(25);//源码中的i就等于startingI,两者地址指向一样
methodB(startingI); // 跳到methodB()
}
// i2 = startingI 传递地址
private void methodB(Integer i2) {
i2 = i2.intValue();//这一句包含了自动拆箱后装箱
// i2.intValue(); 拆箱 25
/* i2 = 25 ; 装箱 Integer.valueOf(25) 返回一个Integer指定的int值的Integer实例
* i2 指向常量池 常量池缓存-128到127(含)范围内的值
*/
//而startingI一直指向new出来的Integer 两者地址指向不同
System.out.println(i2 == startingI); // false
// Integer类中的toString方法已经重写过了, 只比较内容
System.out.println(i2.equals(startingI)); // true
}
public static void main(String[] args) {
new Demo23().methodA(); // 跳到 methodA()
}
}
知识点细化拓展:
自动装箱,分情况:
1、装箱的值在常量池范围内 -128~127 则会装到常量池中
2、装箱的值超出常量池范围,就等同于new了一个新对象
public class Demo23_2 {
public static void main(String[] args) {
Integer i1 = 25;
Integer i2 = 25;
System.out.println(i1 == i2);//均指向常量池 地址内容均一致
i1 = 225; // new Integer(225)
i2 = 225; // new Integer(225)
// 超过了常量池的范围 -128~127
System.out.println(i1 == i2);//地址不同
}
}
06-Integer与int
正确选择为:D
理解解析:
1、对于A、B选项,i的值都改变了,结果一定改变;
2、C选项中将addOne方法改为私有的,则该方法只能在同一类中被访问,Client类中无法访问;
3、对于 Integer 基本数据类型 和 int 包装类型 在使用上是无缝衔接的;但是在定义类的成员变量时,类型还是建议使用包装类型。
07-泛型
Arrays:是数组对应的工具类
Collections:是集合对应的工具类
import java.util.Arrays;
import java.util.List;
public class Demo29 {
public static void main(String[] args) {
int[] arr = {1,2,3,4};
List list = Arrays.asList(arr);
Integer[] arrInt = {1,2,3,4};
List<Integer> list1 = Arrays.asList(arrInt);
// 不指定类型的集合, 就只能转换成Object类型的数组
Object[] arr1 = list.toArray();
// 有泛型的集合, 可以通过传递指定的数组类型来转换
Integer[] arr2 = list1.toArray(new Integer[0]);
}
}
01
正确选择:C
泛型知识点: 泛型 —> <具体类型> 将类型作为参数,也可以是Object,用于规范程序员,以防程序员乱写导致运行失败,因而在编译期就约束好类型。
理解解析:
1、List<String> strings = new ArrayList<>() : 约束程序员只能向strings中添加String类型的对象。
2、题中若没有泛型,strings本质上是一个可以存放所有对象的集合;但是因为添加了泛型,因而编译时指定了类型。
3、A中strings虽然没有内容,但是不影响编译,且若有内容get出来的也是String类型,是正确的,Iterator可泛型可不泛型;
4、若strings没有指定类型,则可以用toArray方法转换为Object类型的数组
02
正确选择:A、C
理解解析:
- 根据泛型,对象类型必须是可比较的,且返回值也得是同一类型,A中123为int类型,“456”字符串类型,都为Object类型,且可比较;同理,123不为String类型,因而B选项不对。
- C选项中,虽然123为int类型,456为Integer类型,但是在使用上是无缝衔接的是一样的,而后对其结果为Integer类型进行拆箱赋值给了i,拆箱后类型为int;D显然不对
03
运行结果为:0042
理解解析:
- 首先,确定程序无编译错误,再者 泛型 只在当前类中有效。
- 虽然集合intlist的泛型类型为Integer,但是进入到append方法时,方法内参数没有带泛型,因而在这个方法内list集合内可以存放任何类型的数据,因而字符串“0042”可以添加成功;若该方法有相同泛型,则添加不成功。
08-Comparable与Comparator
知识点:
1. Comparable<T>: (我)可比较的 compareTo(Object / T obj) 有泛型则用泛型
2. Comparator<T>: 比较器, 作用:比较两个对象 compare(Object / T obj1, Object / T obj2)
正确选择:C
理解解析:
题中TreeMap类的tm对象的键会根据给定的比较器进行排序,且题中键的比较为由大到小。
09-hashCode
正确选择:D
理解解析:
由于HashSet与HashMap都是散列的,没有顺序,且是不允许重复的,因而第一次输出set的大小应是2,排除A、B;令k2.i=1,则是对k2的value值进行更改,set大小不变;set.remove(k1)后,set的大小减一后为1;而因为k2的值目前是1,因而进行set.remove(k2)时,实际上是根据1值去计算hashCode值进行寻找位置的,找的仍然是之前的k1的地址,因而会发现1那个值已经被删了、不存在了,而k2本身仍存在不变,set的大小仍为1。要想把k2删除,只有将k2的值改为2后remove(k2)才行。
10-static代码块与代码块
理解:
public class Demo4 {
public static void main(String[] args) {
final int i;
// 所有的局部变量/临时变量 在使用前都必须初始化
i = 10;
System.out.println(i);
}
}
class Foo {
final int age = 10;// new之后,构造器之前执行
final Foo foo;
//代码块(创建对象之后,构造器之前执行) 在每次调用构造器之前都会执行一次代码块
{
foo = new Foo();
}
//无参构造器
public Foo() { }
//有参构造器
public Foo(int age) { }
}
创建对象的步骤:
第一次创建对象:
- 类加载(在第二次开始创建对象时,就不需要加载了)
- new -> 分配内存空间
- 给直接初始化的变量赋值
- 调用代码块
- 选择一个构造器使用
类加载的执行顺序:(对于前两者,没有先后顺序 ,谁先写就先执行谁)
- 静态代码块 static{ }
- 静态的成员变量 如 static int age = 1
- 静态的成员方法 -> 会先加载到内存中,都是不执行
类加载什么时候会加载:
- 第一次使用类的时候
- 第一次被创建对象
- 调用静态方法
- 调用子类的静态方法
- 第一次创建子类对象
- 调用静态成员变量
- 调用子类的静态成员变量
- 主动加载 Class.forName("类的名字 java.lang.String")
例题:重要
结果为:
理解解析:
首先,在类B中有主方法,而主方法是静态方法,调用静态方法前会先进行类加载,而B又是A的子类,因而会先加载类A中的静态代码块,然后再加载B中的静态代码块;接着执行主方法new B对象,由于B继承A ,因而会先new A,则会调用A的构造器,而每一次调用构造器之前都会先执行一次代码块,A new完后new B,同理,接着执行B的代码块而后调用B的构造器。