“>>”(有符号)右移,如果为正,高位补”0“;
----------------------------- 如果为负,高位补”1“。
”>>>“无符号右移/逻辑右移,无论正负都补"0"。(不管原数的符号位,就是把32位数右移,最后高位补"0")
没有无符号左移。Java中int类型是4字节,32位,所以左移时要注意。(不是看当前数字有几位)。负数是以补码形式存储的,所以负数的左移和右移要先看补码左右移的结果,
然后根据”补码的补码是原码“求得最终结果。 (要注意求反码时:符号位不变,其他位取反。)
比如:-10<<2:(加粗的"1"是符号位) -10的原码是000…0(27个0)11010 ,
------------------------------------------------------ 反码是111…1(27个1)10101,
----------------------------- ------------------------ 补码是111…1(27个1)10110,
将补码左移2位的结果是:
--------------------------------------------------------------- 111…1(25个1)1011000 接下来需要对补码求补码得到最终结果:
----------------------------- 反码是000…0(25个0)1100111,
----------------------------- 补码是000…0(25个0)1101000,即-4
接下来求-10>>2,有符号右移,补码是111…1(27个1)10110,
将补码有符号移2位(本身是负数,高位补"1")的结果是:
----------------------------- 111…1(27个1)11101 接下来根据补码的补码为源码,先除符号位外按位取反得反码:
----------------------------- 000…0(27个0)10010
-------------加1得补码:000…0(27个0)10011,即-3
最后求-10>>>2,无符号右移,补码是111…1(27个1)10110,
由于无符号位右移的规定结果必须是正数,所以原符号位的“1”被自动转换为对应的数值,不再代表符号。且,正数的补码是本身,所以不变。 将补码无符号右移2位(高位补"0")的结果是:
----------------------------------------------- 00111…1(28个1)01
即1073741821。
计算机里,是这么运算补码的:对于负数,先取绝对值,然后求反,加一
-128 -> 128 -> 1000 0000 -> 0111 1111 -> 1000 0000
所以,1字节也就是 8位有符号的整数取值范围的补码表示:
1000 0000 到 0000 0000, 再到 0111 1111
即 -128 到 0, 再到 127 ,也就是 -128 ~ +127 。
(可以这么理解: 计算机为数据类型分配了 n 位,超过 n 位的数值会被自动舍弃,现在计算机系统中采用的补码,克服了“原码中存在+0和-0”的情况,仅表现为一个0,1000 0000感觉是 -0,其实是 -128 的补码。)
B.System.arraycopy
public static native void arraycopy
(Object src, int srcPos,Object dest, int destPos,int length);
是本地方法。
这篇https://blog.csdn.net/qq_34834846/article/details/97174521说:
arraycopy方法上有@HotSpotIntrinsicCandidate(然鹅我没找到不知道为什么 qaq)为了提升性能,在JVM里对@HotSpotIntrinsicCandidate
注解的方法进行了手写,这里要提一个叫*JNI(Java Native Interface)*的东西,普通的native方法( 比如C选项Array.copyOf方法 )编译后还要通过JNI再次编译成.cpp文件才能执行。
而有 @HotSpotIntrinsicCandidate这个注解的方法在JVM里就是用.cpp文件写好的,所以就跳过了JNI阶段,所以速度就能提升,这也是System.arraycopy()速度冠绝群雄的原因。
C,Arrays.copyOf
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]:(T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
该方法还是调用的System.arraycopy,效率不如B高 。
D.使用clone方法
protected native Object clone() throws
CloneNotSupportedException;
List
中T
的实际类型。 × 可以通过反射机制获取 。下列说法正确的是:
A.ConcurrentHashMap使用synchronized关键字保证线程安全 ×
ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键/值对;Segment(继承自 ReentrantLock )用来充当锁的角色,每个 Segment 对象守护整个散列映射表的若干个桶。每个桶是由若干个 HashEntry 对象链接起来的链表。即一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素。
使用synchronized关键字来保证同步的是HashTable。
B.HashMap实现了Collection接口 ×
HashMap implements Map而非Collection。
C.Array.asList方法返回java.util.ArrayList对象 ×
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
返回的是ArrayList对象,但是不是java.util包中的ArrayList,而是Arrays的一个内部类:
private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
D.SimpleDateFormat是线程不安全的 √
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
从calendar.setTime(date);
可知:多个线程共享SimpleDateFormat实例时,对Calendar对象的更改会相互影响,因此产生了线程安全问题。
Java程序初始化的顺序:
(1)静态优先于非静态,
(2)父类优先于子类。
且(1)优先级大于(2)。
能正确实现GBK编码字节流到UTF-8编码字节流:
byte[] src,dst;
dst=new String(src,"GBK").getbytes("UTF-8");
String类的构造方法:
(1) String() :初始化新创建的 String对象,使其表示空字符序列。
(2) String(byte[] bytes) :通过使用平台的默认字符集解码指定的字节数组来构造新的 String 。
(3) String(byte[] bytes, Charset charset) :
构造一个新的String,由指定的字节数组解码charset 。
传入(src,:“GBK”)构造的String对象就是由字节数组src解码GBK。
(4) String(byte[] bytes, int offset, int length) :通过使用平台的默认字符集解码指定的字节子阵列来构造新的 String 。
(5) String(byte[] bytes, int offset, int length, Charset charset) :构造一个新的String通过使用指定的指定字节子阵列解码charset 。
(6) String(byte[] bytes, int offset, int length, String charsetName) :构造一个新的 String通过使用指定的字符集解码指定的字节子阵列。
(7) String(byte[] bytes, String charsetName) :构造一个新的String由指定用指定的字节的数组解码charset 。
(8) String(char[] value) :分配一个新的 String ,以便它表示当前包含在字符数组参数中的字符序列。
(9)String(char[] value, int offset, int count) :分配一个新的 String ,其中包含字符数组参数的子阵列中的字符。
(10) String(int[] codePoints, int offset, int count) :分配一个新的 String ,其中包含 Unicode code point数组参数的子阵列中的 字符 。
(11) String(String original) :初始化新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本。
(12) String(StringBuffer buffer) :分配一个新的字符串,其中包含当前包含在字符串缓冲区参数中的字符序列。
(13)String(StringBuilder builder) :分配一个新的字符串,其中包含当前包含在字符串构建器参数中的字符序列。
接下来调用String类的getBytes方法,传入Charset类型的参数,返回值是byte[ ]。
byte[] getBytes(Charset charset)
使用给定的charset将该String编码为字节序列,将结果存储到新的字节数组中。
注意:
String类没有frombytes方法,
也没有encode方法。
public class E20200129 {
public static void main(String[] args) {
System.out.println(new B().getValue());
}
static class A
{protected int value;
public A(int v)
{
setValue(v);
}
public void setValue(int value)
{this.value=value;}
public int getValue()
{try
{
value++;
return value;
}
catch (Exception e)
{System.out.println(e.toString());
}
finally {
this.setValue(value);
System.out.println(value);
}
return value;
}
}
static class B extends A
{public B()
{super(5);
setValue(getValue()-3);
}
public void setValue(int value)
{super.setValue(2*value);}
}
}
运行结果:
22
34
17
分析该代码执行过程:
不管什么程序都是先看psvm主方法,先new B()
,调用B类的构造方法,而B类是继承A类的,要先会执行A类的构造方法,B类构造中是 super(5)
, 调用A类构造方法,传入的参数是5 ,A类构造方法中是调用了setValue
方法,注意!!! 这里**就近原则**,调用的是B类的setValue方法,而B类的serValue方法中,又是调用的父类A的setValue
方法,传入的参数是 2*5=10,那么 B类的变量value=10
,接下来继续执行 B类构造,调用setValue(getValue()-3)
,先要知道getValue()的值,该方法只有A类有,先执行try块,value++
: value=value+1=10+1=11
,在return 11之前,会执行finally块的内容(但是11已经被拷贝了,value变量值改变不会影响返回值11 )在finally块中,调用B类的setValue(11)
方法,调用A类的setValu(2*11=22)
,这时B类的value值变为22,finally块会输出22 ,接下来,getValue()返回值是11 ,然后调用B类的setValue(11-3=8)
,会执行A类的setValue(2*8=16)
,这时value变成16 。
到这时B类的构造方法就执行完毕,回到主方法中,新产生的B类对象调用getValue方法,只有父类A有,先执行try块,value++
: value=value+1=16+1=17
,在return 17 前会执行finally块 ,调用B类的setValue(17)方法,而B类的setValue(17)方法的实现是调用A类的setValue( 2*17=34 )
,这时value=34 ,在finally 块中需要输出value,所以第二个输出的数是 34 ,fially块执行完毕17就可以return 了,所以在主方法中会输出17 。因此运行结果就是 “22 34 17”
子类的构造方法中,如果没有显式地调用父类的构造方法,就会默认调用父类的无参构造。(所以父类得有无参构造,如果父类只提供了有参构造,编译就会报错。)比如:
class Person
{
String name="No name";
public Person(String nm)
{name=nm;}
}
class Employee extends Person
{String empID="0000";
public Employee(String id)
{ empID=id;}}
public class F20200204 {
public static void main(String[] args) {
Employee e=new Employee("123");
System.out.println(e.empID);
}}
改正方法:
(1)父类添加一个无参构造,子类在调用父类无参构造后就会正常执行它自己的构造方法。
class Person
{
String name="No name";
public Person(String nm)
{name=nm;}
public Person()
{}
}
(2)在子类中显式调用父类的有参构造。
class Employee extends Person
{String empID="0000";
public Employee(String id)
{
super(id);
empID=id;}
}
int i=0;
Integer j=new Integer(0);
System.out.println(i==j);
System.out.println(j.equals(i));
运行结果:
true
true
包装类和 String 类相像,也有常量池。
比如 Integer i=40
在编译时会执行将代码封装成:
Integer i=Integer.valueOf( 40 );
f在Integer类的 valueOf 方法中有个内部类 IntegerCache ,维护了一个 Integer 数组 Cache .长度为 256 ( -128~ 127 ) 。
f这道题,基本型和包装类进行 “ ==
” 运算符的比较,基本型封装型将会自动拆箱变为基本型后再进行比较,(可以这么想:基本型进行“ ==
”比较时,比较的是值。基本类型变量在栈中直接存储值;
引用类型变量在栈中存储的是地址,来指向堆内存的实例
。)。
而包装类变量调用 equals 方法时,看源码:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
f所以包装类变量.equals(基本型) ,会先把基本型变量装箱为包装类,然后先比较包装类类型,都是Integer类,OK,再比较值。
对于Integer i = value;
就相当于 Integer i= Integer.valueOf(xxx);
valueOf方法先判断传入参数的值,如果是在 -128 到 127 之间,不会去堆中创建对象,而是直接返回 IntegerCache 中的 cache 值;这个 IntegerCache 是 Integer 的静态内部类 :private static class IntegerCache
,该类中有一个 cache 数组,放 Integer 对象,该类的静态块中完成了数组的初始化。 否则,new 一个 Integer 返回 。
因此:
Integer i1=Integer.valueOf(59);
Integer i2=new Integer(59);
System.out.println(i1==i2);
结果是 false,因为 i1 不会 new 对象,是从 Cache 数组里取的。
Integer i=Integer.valueOf(22);
Integer j=Integer.valueOf(22);
System.out.println(i==j);
f结果是 true,取的是同一对象。
Integer i=new Integer(22);
Integer j=new Integer(22);
System.out.println(i==j);
f结果是 false ,因为在堆中开辟了不同空间。