所在的位置: 程序员 >> 程序人生 >> Java基础

Java基础

Java基础

?Java基础[1]

?一、数据类型[2]

?基本类型[3]?包装类型[4]?缓存池[5]

?二、String[6]

?概览[7]?不可变的好处[8]?String,StringBufferandStringBuilder[9]?StringPool[10]?newString("abc")[11]

?三、运算[12]

?参数传递[13]?float与double[14]?隐式类型转换[15]?switch[16]

?四、关键字[17]

?final[18]?static[19]

?五、Object通用方法[20]

?概览[21]?equals()[22]?hashCode()[23]?toString()[24]?clone()[25]

?六、继承[26]

?访问权限[27]?抽象类与接口[28]?super[29]?重写与重载[30]

?七、反射[31]?八、异常[32]?九、泛型[33]?十、注解[34]?十一、特性[35]

?Java各版本的新特性[36]?Java与C++的区别[37]?JREorJDK[38]

?参考资料[39]

一、数据类型基本类型

?byte/8?char/16?short/16?int/32?float/32?long/64?double/64?boolean/~

boolean只有两个值:true、false,可以使用1bit来存储,但是具体大小没有明确规定。JVM会在编译时期将boolean类型的数据转换为int,使用1来表示true,0表示false。JVM支持boolean数组,但是是通过读写byte数组来实现的。

?PrimitiveDataTypes[40]?TheJava?VirtualMachineSpecification[41]

包装类型

基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。

Integerx=2;//装箱调用了Integer.valueOf(2)inty=x;//拆箱调用了X.intValue()

?AutoboxingandUnboxing[42]

缓存池

newInteger()与Integer.valueOf()的区别在于:

?newInteger()每次都会新建一个对象;?Integer.valueOf()会使用缓存池中的对象,多次调用会取得同一个对象的引用。

Integerx=newInteger();Integery=newInteger();System.out.println(x==y);//falseIntegerz=Integer.valueOf();Integerk=Integer.valueOf();System.out.println(z==k);//true

valueOf()方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。

publicstaticIntegervalueOf(inti){if(i=IntegerCache.lowi=IntegerCache.high)returnIntegerCache.cache[i+(-IntegerCache.low)];returnnewInteger(i);}

在Java8中,Integer缓存池的大小默认为-~。

staticfinalintlow=-;staticfinalinthigh;staticfinalIntegercache[];static{//highvaluemaybeconfiguredbypropertyinth=;StringintegerCacheHighPropValue=sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if(integerCacheHighPropValue!=null){try{inti=parseInt(integerCacheHighPropValue);i=Math.max(i,);//MaximumarraysizeisInteger.MAX_VALUEh=Math.min(i,Integer.MAX_VALUE-(-low)-1);}catch(NumberFormatExceptionnfe){//Ifthepropertycannotbeparsedintoanint,ignoreit.}}high=h;cache=newInteger[(high-low)+1];intj=low;for(intk=0;kcache.length;k++)cache[k]=newInteger(j++);//range[-,]mustbeinterned(JLS75.1.7)assertIntegerCache.high=;}

编译器会在自动装箱过程调用valueOf()方法,因此多个值相同且值在缓存池范围内的Integer实例使用自动装箱来创建,那么就会引用相同的对象。

Integerm=;Integern=;System.out.println(m==n);//true

基本类型对应的缓冲池如下:

?booleanvaluestrueandfalse?allbytevalues?shortvaluesbetween-and?intvaluesbetween-and?charintherange\uto\uF

在使用这些基本类型对应的包装类型时,如果该数值范围在缓冲池范围内,就可以直接使用缓冲池中的对象。

在jdk1.8所有的数值类缓冲池中,Integer的缓冲池IntegerCache很特殊,这个缓冲池的下界是-,上界默认是,但是这个上界是可调的,在启动jvm的时候,通过-XX:AutoBoxCacheMax=size来指定这个缓冲池的大小,该选项在JVM初始化的时候会设定一个名为java.lang.IntegerCache.high系统属性,然后IntegerCache初始化的时候就会读取该系统属性来决定上界。

StackOverflow:DifferencesbetweennewInteger(),Integer.valueOf()andjust[43]

二、String概览

String被声明为final,因此它不可被继承。(Integer等包装类也不能被继承)

在Java8中,String内部使用char数组存储数据。

publicfinalclassStringimplementsjava.io.Serializable,ComparableString,CharSequence{/**Thevalueisusedforcharacterstorage.*/privatefinalcharvalue[];}

在Java9之后,String类的实现改用byte数组存储字符串,同时使用coder来标识使用了哪种编码。

publicfinalclassStringimplementsjava.io.Serializable,ComparableString,CharSequence{/**Thevalueisusedforcharacterstorage.*/privatefinalbyte[]value;/**Theidentifieroftheencodingusedtoencodethebytesin{

codevalue}.*/privatefinalbytecoder;}

value数组被声明为final,这意味着value数组初始化之后就不能再引用其它数组。并且String内部没有改变value数组的方法,因此可以保证String不可变。

不可变的好处

1.可以缓存hash值

因为String的hash值经常被使用,例如String用做HashMap的key。不可变的特性可以使得hash值也不可变,因此只需要进行一次计算。

2.StringPool的需要

如果一个String对象已经被创建过了,那么就会从StringPool中取得引用。只有String是不可变的,才可能使用StringPool。

3.安全性

String经常作为参数,String不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果String是可变的,那么在网络连接过程中,String被改变,改变String的那一方以为现在连接的是其它主机,而实际情况却不一定是。

4.线程安全

String不可变性天生具备线程安全,可以在多个线程中安全地使用。

ProgramCreek:WhyStringisimmutableinJava?[44]

String,StringBufferandStringBuilder

1.可变性

?String不可变?StringBuffer和StringBuilder可变

2.线程安全

?String不可变,因此是线程安全的?StringBuilder不是线程安全的?StringBuffer是线程安全的,内部使用synchronized进行同步

StackOverflow:String,StringBuffer,andStringBuilder[45]

StringPool

字符串常量池(StringPool)保存着所有字符串字面量(literalstrings),这些字面量在编译时期就确定。不仅如此,还可以使用String的intern()方法在运行过程将字符串添加到StringPool中。

当一个字符串调用intern()方法时,如果StringPool中已经存在一个字符串和该字符串值相等(使用equals()方法进行确定),那么就会返回StringPool中字符串的引用;否则,就会在StringPool中添加一个新的字符串,并返回这个新字符串的引用。

下面示例中,s1和s2采用newString()的方式新建了两个不同字符串,而s3和s4是通过s1.intern()和s2.intern()方法取得同一个字符串引用。intern()首先把"aaa"放到StringPool中,然后返回这个字符串引用,因此s3和s4引用的是同一个字符串。

Strings1=newString("aaa");Strings2=newString("aaa");System.out.println(s1==s2);//falseStrings3=s1.intern();Strings4=s2.intern();System.out.println(s3==s4);//true

如果是采用"bbb"这种字面量的形式创建字符串,会自动地将字符串放入StringPool中。

Strings5="bbb";Strings6="bbb";System.out.println(s5==s6);//true

在Java7之前,StringPool被放在运行时常量池中,它属于 代。而在Java7,StringPool被移到堆中。这是因为 代的空间有限,在大量使用字符串的场景下会导致OutOfMemoryError错误。

?StackOverflow:WhatisStringinterning?[46]?深入解析String#intern[47]

newString("abc")

使用这种方式一共会创建两个字符串对象(前提是StringPool中还没有"abc"字符串对象)。

?"abc"属于字符串字面量,因此编译时期会在StringPool中创建一个字符串对象,指向这个"abc"字符串字面量;?而使用new的方式会在堆中创建一个字符串对象。

创建一个测试类,其main方法中使用这种方式来创建字符串对象。

publicclassNewStringTest{publicstaticvoidmain(String[]args){Strings=newString("abc");}}

使用javap-verbose进行反编译,得到以下内容:

//...Constantpool://...#2=Class#18//java/lang/String#3=String#19//abc//...#18=Utf8java/lang/String#19=Utf8abc//...publicstaticvoidmain(java.lang.String[]);descriptor:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=3,locals=2,args_size=10:new#2//classjava/lang/String3:dup4:ldc#3//Stringabc6:invokespecial#4//Methodjava/lang/String."init":(Ljava/lang/String;)V9:astore_1//...

在ConstantPool中,#19存储这字符串字面量"abc",#3是StringPool的字符串对象,它指向#19这个字符串字面量。在main方法中,0:行使用new#2在堆中创建一个字符串对象,并且使用ldc#3将StringPool中的字符串对象作为String构造函数的参数。

以下是String构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制value数组内容,而是都会指向同一个value数组。

publicString(Stringoriginal){this.value=original.value;this.hash=original.hash;}三、运算参数传递

Java的参数是以值传递的形式传入方法中,而不是引用传递。

以下代码中Dogdog的dog是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。

publicclassDog{Stringname;Dog(Stringname){this.name=name;}StringgetName(){returnthis.name;}voidsetName(Stringname){this.name=name;}StringgetObjectAddress(){returnsuper.toString();}}

在方法中改变对象的字段值会改变原对象该字段值,因为引用的是同一个对象。

classPassByValueExample{publicstaticvoidmain(String[]args){Dogdog=newDog("A");func(dog);System.out.println(dog.getName());//B}privatestaticvoidfunc(Dogdog){dog.setName("B");}}

但是在方法中将指针引用了其它对象,那么此时方法里和方法外的两个指针指向了不同的对象,在一个指针改变其所指向对象的内容对另一个指针所指向的对象没有影响。

publicclassPassByValueExample{publicstaticvoidmain(String[]args){Dogdog=newDog("A");System.out.println(dog.getObjectAddress());//Dog

cfunc(dog);System.out.println(dog.getObjectAddress());//Dog

cSystem.out.println(dog.getName());//A}privatestaticvoidfunc(Dogdog){System.out.println(dog.getObjectAddress());//Dog

cdog=newDog("B");System.out.println(dog.getObjectAddress());//Dog

74aSystem.out.println(dog.getName());//B}}

StackOverflow:IsJava“pass-by-reference”or“pass-by-value”?[48]

float与double

Java不能隐式执行向下转型,因为这会使得精度降低。

1.1字面量属于double类型,不能直接将1.1直接赋值给float变量,因为这是向下转型。

//floatf=1.1;

1.1f字面量才是float类型。

floatf=1.1f;隐式类型转换

因为字面量1是int类型,它比short类型精度要高,因此不能隐式地将int类型向下转型为short类型。

shorts1=1;//s1=s1+1;

但是使用+=或者++运算符会执行隐式类型转换。

s1+=1;s1++;

上面的语句相当于将s1+1的计算结果进行了向下转型:

s1=(short)(s1+1);

StackOverflow:WhydontJavas+=,-=,*=,/=


转载请注明:http://www.xxcyfilter.com/cxrs/cxrs/11866.html