Java架构师成长之道之Java数据存储
Java架构师成长之道2.1 标识符
2.1.1 标识符概述
标识符(Identifier)就是在编写Java程序时给包(package)、类(classs)、成员变量(Member Varriable)、局部变量(Local Varriable)、成员方法(Member Method)等元素命名的字符串(单词),例如在编写第一个Java程序HellWorld时定义的class HelloWorld
的HelloWorld就是标识符,用于定义类名。
2.1.2 关键字和保留字
关键字是被Java赋予了特殊含义的单词,例如public class等等,所有的关键字都是小写字母
,官网给出了所有关键字。而在IntelliJ IDEA中,Java的关键字是橙色的。
保留字是现有的Java版本尚未使用,但是未来可能作为关键字使用,例如goto,const。
2.1.3 标识符的命名规范
软件开发是大型团队的合作,在开发Java应用程序时,使用标识符需要严格遵守如下规范:
标识符可以由26个英文字母(大小写)、数字、下滑线或者美元符号($)组成,但是不能以数字、美元符号($)和下划线开头,也不能以美元符号和下划线结束。标识符不能是关键字或者保留字,但是能包含关键字和保留字,例如staticMethod
标识符禁止使用中英文混合命名,更不允许直接使用中文的命名方式,建议命名时见名知意。类名使用UpperCamelCase风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵 从驼峰形式常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使 用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
读者可以参考JDK提供常用的API,例如String类的命名方式
String类所在的包是java.lang包
String类中的常量名命名
String类中的方法命名
package net.ittimeline.java.core.syntax; //包的命名/*** Java标识符** @author liuguanglei 18601767221@* @create -07-22 16:21* @website * @since JDK11.03*/public class IdentifierTest {public static void main(String[] args) {CustomerInfo customerInfo = new CustomerInfo();customerInfo.setCustomerName("tony");System.out.println("customerInfo name is " + customerInfo.getCustomerName());System.out.println("customerInfo id card id " + CustomerInfo.CUSTOMER_ID_CARD);}}/*** 类的命名*/class CustomerInfo {/*** 成员变量的命名*/private String customerName;/*** 常量的命名*/public static final String CUSTOMER_ID_CARD = "430904458902219877";/*** 方法的命名** @return*/public String getCustomerName() {return this.customerName;}public void setCustomerName(String customerName) {this.customerName = customerName;}}
2.2 变量
2.2.1 变量概述
变量代表内存的某个存储区域,该区域的数据可以在同一类型之内随着业务逻辑不断发生变化,变量作为最基本的存储数据的单元,由变量名、变量类型和变量值组成。通过操作变量,就可以方便的操作内存中的数据。
2.2.2 变量使用
Java是强类型的语言,在使用变量前必须指定变量的类型,而且必须声明以及初始化赋值,否则程序会出现编译错误。
变量声明初始化赋值的格式如下所示
数据类型 变量名=变量值;
其中数据类型可以是基本数据类型和引用数据类型,变量名需要严格准守标识符命名规范,=表示赋值运算符,将右边的值赋值给左边的变量,Java使用==
进行相等性判断,变量值可以是对应数据类型所能存储的数值。例如int age=29;
变量声明初始化赋值后,Java会根据变量的数据类型在内存中开辟对应的空间存储数据。
package net.ittimeline.java.core.syntax;/*** Java变量* <p>* 变量的声明格式* <p>* 变量类型 变量名=变量值;** @author liuguanglei 18601767221@* @create -07-22 16:55* @website * @since JDK11.03*/public class VarriableTest {public static void main(String[] args) {//变量的声明赋值int age = 28;System.out.println("current age = " + age);//修改变量的值//同一时刻,变量只有一个值,新值会覆盖旧值age = 29;System.out.println("next year age = " + age);//如果使用了未声明的变量,会发生编译错误//编译时找不到变量name的声明// System.out.println("my name is "+name);String name;//局部变量声明之后还要初始化赋值才能使用// System.out.println("my name is "+name);//score变量是在varriableScope方法中定义的局部变量,不能在main方法中使用//System.out.println("score = "+score);//同一作用域内下变量名不能重名 name之前已经声明过// String name;}/*** 变量的作用域*/public void varriableScope() {//变量的作用域在当前声明的{}之内 方法中的变量需要手动初始化赋值int score = 99;}}
在方法中声明的变量叫局部变量,在类中声明的变量叫成员变量。
局部变量需要手动初始化,成员变量由Java根据变量的数据类型进行自动初始化。
package net.ittimeline.java.core.syntax;/*** 局部变量和成员变量的初始化** @author liuguanglei 18601767221@* @create -07-22 17:40* @website * @since JDK11.03*/public class VarriableInitTest {public static void main(String[] args) {Student student=new Student();}}/*** 自定义类Student*/class Student{/**定义三个成员变量*/double score;String name;int studentNo;/*** 在构造器中输出成员变量的默认值*/Student(){System.out.println("score = "+score);System.out.println("name = "+ name);System.out.println("studentNo = "+studentNo);}}
2.2.3 变量的分类
变量按照不同的作用域可以分为局部变量、成员变量和类变量。
其中在方法或者代码块中声明的变量叫局部变量,其作用域从属于方法或者代码块。
package net.ittimeline.java.core.syntax.varriable;/*** 局部变量的使用** @author liuguanglei 18601767221@* @create -07-26 10:39* @website * @since JDK11.03*/public class LocalVariableTest {public static void main(String[] args) {{int age = 29;}//这里无法使用代码块中的aqe变量,因为age只能在{}作用域内访问//System.out.println("age = "+age);int score=100;System.out.println("score = "+score);}}
在方法外部,类中声明的变量叫成员变量,其作用域从属于该类的对象。成员变量需要实例化对象,通过对象的引用.变量名访问。
package net.ittimeline.java.core.syntax.varriable;/*** 成员变量的使用** @author liuguanglei 18601767221@* @create -07-26 10:40* @website * @since JDK11.03*/public class MemberVariableTest {String name;int age;public static void main(String[] args) {//实例化MemberVariableTest对象MemberVariableTest memberVariableTest =new MemberVariableTest();//通过对象的引用memberVariableTest.name访问成员变量memberVariableTest.name="tony";memberVariableTest.age=29;System.out.println("name is "+ memberVariableTest.name +" age is "+ memberVariableTest.age);}}
而在方法外部,类中声明并且使用static关键字修饰的变量叫做类变量,其作用域从属于该类,可以通过类名.变量名访问。
package net.ittimeline.java.core.syntax.varriable;/*** 静态变量的使用** @author liuguanglei 18601767221@* @create -07-26 10:40* @website * @since JDK11.03*/public class StaticVarriableTest {public static void main(String[] args) {System.out.println(User.ID_CARD);//静态变量可以直接由类名.变量名访问System.out.println(User.remark);}}class User{static final String ID_CARD="421023199302888588";/*** 静态变量由Java负责初始化*/static String remark;}
其中局部变量需要手动初始化赋值,成员变量和静态变量不需要手动初始化赋值,Java会自动根据其类型进行初始化赋值。
2.2.4 变量的交换
在某些场景中(例如给数据排序)会使用到变量的交换。
在Java中,变量的交换可以使用中间变量、算术运算以及亦或运算实现。
package net.ittimeline.java.core.syntax.varriable;/*** 变量交换** @author liuguanglei 18601767221@* @create -07-26 10:17* @website * @since JDK11.03*/public class VarriableSwapTest {public static void main(String[] args) {int left=5;int right=10;//通过引入中间变量tmp实现变量的交换System.out.println("通过引入中间变量tmp实现变量的交换");System.out.println("交换变量之前");println(left,right);// tmp=5 left=5int tmp=left;//left=10,right=10left=right;right=tmp;System.out.println("交换变量之后");println(left,right);//通过算术运算+ -实现交换System.out.println("通过算术运算实现交换");left=5;right=10;System.out.println("交换变量之前");println(left,right);//left=15 right=10left=left+right;//right=5 left=15right=left-right;//left=10 right=5left=left-right;System.out.println("交换变量之后");println(left,right);//通过异或运算实现变量交换System.out.println("通过异或运算实现变量交换");left=5;right=10;System.out.println("交换之前");println(left,right);left=left^right;right=left^right;left=left^right;System.out.println("交换之后");println(left,right);}/*** 输出left和变量的值* @param left* @param right*/public static void println(int left,int right){System.out.println("left = "+left+" right = "+right);}}
2.3 数据类型
在计算机底层数据都是使用二进制表示的,使用起来不太方便,因此引入数据类型的概念,数据类型用于对不同的数据进行分类,其原因在于
不同的类型所能存储的数据类型不同,不同表示的数据范围(极限)也不同不同的数据类型 在内存中占据的空间(字节数量)也不同不同的数据类型参与不同的运算。
Java的数据类型分为基本数据类型(Primitive Type)和引用数据类型(Reference Type)两大类,如下列表所示,目前只讨论基本数据类型
其中基本数据类型都有数组类型,数组就是一片连续的存储空间。
而基本数据类型不具有面向对象的编程机制,也没有对象的特性:没有成员变量、方法可以被调用,因此Java八种基本数据类型对应的引用类型,也被称为包装类,如下表格所示。
在日常开发中常用的基本类型有byte,int,long和boolean。
2.4 JDK1.5新特性 自动拆箱和装箱
JDK1.5以后提供了自动拆箱和自动装箱的特性实现了基本数据类型和包装类的转换。
自动拆箱:将引用数据类型(例如Long)转换为基本数据类型(例如long)
自动装箱:将基本数据类型(例如byte)转换为引用数据类型(例如Byte)
package net.ittimeline.java.core.object;/*** JDK5新特性:自动拆箱和自动装箱* 自动拆箱:引用数据类型转换为基本数据类型* 自动装箱:基本数据类型转换为引用数据类型* Java八种基本数据类型以及包装类想换转换* @author liuguanglei 18601767221@* @create -07-22 10:40* @website * @since JDK11.03*/public class AutoboxingTest {public static void main(String[] args) {//自动装箱Byte by = 1;//自动拆箱byte bt = by;System.out.println("byte = " + bt);Short sh = 1;short s = sh;System.out.println("short = " + s);Integer in = 1;int i = in;System.out.println("int = " + i);Long lo = 1L;long l = lo;System.out.println("long = " + l);Boolean bo = true;boolean b = bo;System.out.println("boolean = " + b);Character ch = 'x';char c = ch;System.out.println("char = " + c);Float fl = 1.0f;float f = fl;System.out.println("float = " + f);Double db = 1.0d;double d = db;System.out.println("double = " + d);}}
2.5 整型
2.5.1 Java整型的极限
整型是日常生活中常用的类型,例如人的年龄,大楼的层数等等。
Java中的整型按照占据不同内存大小,可以分为byte,short,int,long四种类型。
如下图所示,可以通过查看其包装类Byte的源码获取其表示数据的范围
以及Byte占据的位数和字节数
其他类型可以使用同样的方式通过查看包装类的源码获取存储数据的表示范围以及占据的字节带小。
如下应用程序所示,使用了四种整数类型的包装类获取其对应的数据存储范围以及占据的字节数量。
package net.ittimeline.java.core.syntax.type;/*** 整数的极限以及内存大小** @author liuguanglei 18601767221@* @create -07-26 12:59* @website * @since JDK11.03*/public class IntegerLimitsTest {public static void main(String[] args) {//这里的%d表示整数占位符,会被对应的数值替换System.out.printf("byte所能存储的最大值是%d,所能存储的最小值是%d,占据的字节数量是%d \n",Byte.MAX_VALUE,Byte.MIN_VALUE,Byte.BYTES);System.out.printf("short所能存储的最大值是%d,所能存储的最小值是%d,占据的字节数量是%d \n",Short.MAX_VALUE,Short.MIN_VALUE,Short.BYTES);System.out.printf("int所能存储的最大值是%d,所能存储的最小值是%d,占据的字节数量是%d \n",Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.BYTES);System.out.printf("long所能存储的最大值是%d,所能存储的最小值是%d,占据的字节数量是%d \n",Long.MAX_VALUE,Long.MIN_VALUE,Long.BYTES);}}
在使用对应的数据类型时,需要注意不要超过其存储的极限(即最大值和最小值),否则程序会发生错误。
2.5.2 大整数BigInteger
日常开发中使用的整数是byte,int,long以及对应的包装类,整数默认是int,而long类型的常量值后面需要添加L后缀,如果想要使用比Long更大范围的数据,可以使用java.math包中的BigInteger类。
package net.ittimeline.java.core.syntax.type;import java.math.BigInteger;/*** Java* Java整型有byte short int long 四种类型,常用的有byte,int和long* 整数默认是int** @author liuguanglei 18601767221@* @create -07-22 18:54* @website * @since JDK11.03*/public class IntegerTest {public static void main(String[] args) {byte bt=-128;System.out.println("bt = "+bt);//int INT_MAX_VALUE=Integer.MAX_VALUE;System.out.println("int max value is "+INT_MAX_VALUE);//long类型的变量值如果超过了int的表示范围,需要使用L后缀//全球人口数量long totalPopulation=76_0000_0000L;System.out.println("totalPopulation = "+totalPopulation);//如果想要表示宇宙的星球数量,假如是1万亿亿 long类型也不能够存储//long starQuantity=1_0000_0000_0000_0000_0000L;//此时可以使用JDK提供的BigInteger来存储大整数String starQuantityStr="100000000000000000000";BigInteger starQuantity=new BigInteger(starQuantityStr);System.out.println("starQuantity = "+starQuantity);}}
2.5.3 整数的四种进制类型
在Java中,整数支持四种进制类型,分别是二进制、八进制、十进制和十六进制。
其中JDK1.7以后支持二进制,以0b开头,而八进制是以0开头,十六进制是以0x开头
package net.ittimeline.java.core.syntax.type;/*** 整数的四种进制类型** @author liuguanglei 18601767221@* @create -07-26 13:10* @website * @since JDK11.03*/public class IntegerRadix {public static void main(String[] args) {byte binary=0b1100;System.out.println("二进制变量binary = "+binary);short oct=012;System.out.println("八进制进制变量oct = "+oct);int value=12;System.out.println("十进制变量value = "+value);long hex=0x12;System.out.println("十六进制变量hex = "+hex);}}
在日常企业开发中基本使用的都是十进制的整数,但是在JDK源码以及其他场景中中会使用到其他进制,如下图所示,Integer类就使用了十六进制的常量来表示int的取值范围。
关于进制的更多的内容,可以参考Java架构师成长之道之计算机组成原理计算篇的3.1进制运算基础以及3.2进制之间的转换相关的内容
2.5.4 数值字面量使用下划线
如果Java源文件中存在着很长的数字字面量(即变量值),不易阅读,此时可以使用JDK7以后新增的特性:数值字面量使用下划线来分割数值,使其更加容易阅读。
我们可以在整数和浮点数的数值之间使用下划线,编译器在编译时会删除这些下划线。
package net.ittimeline.java.core.syntax.type;/*** 数值字面量使用下划线** @author liuguanglei 18601767221@* @create -07-26 14:51* @website * @since JDK11.03*/public class IntegerFloatUnderScoreTest {public static void main(String[] args) {//数值字面量使用下划线分割,更加容易阅读,编译器在编译时会删除下划线long number=12_0000_0000_0000L;//因此在输出number变量的值时不会看到下划线System.out.println("number = "+ number);double value=3.1415_926;System.out.println("value = "+value);}}
2.6 Java浮点型
2.6.1 浮点型的两种表示方式
浮点数就是生活中常用的小数,例如银行的利率,股市的涨跌等等都是浮点数。
Java中的浮点数类型分为单精度浮点型float和双精度浮点型double两种,默认是double类型,浮点数可以使用十进制和科学计数法两种表示方式。
package net.ittimeline.java.core.syntax.type;/*** Java浮点型* 数学意义上的小数就是浮点型* float和double是Java的两种浮点类型* 浮点数默认是double,如果想使用float,变量值后面跟f后缀* <p>* 浮点数在进行运算时存在着误差** @author liuguanglei 18601767221@* @create -07-22 19:21* @website * @since JDK11.03*/public class FloatTest {public static void main(String[] args) {//float类型的变量值后面要加上f后缀,否则会出现编译错误float flt = 12.0f;//浮点类型默认是doubledouble dbl = 12.0;//使用科学计数法表示浮点数double value = 1.2e1;System.out.println("flt = " + flt);System.out.println("dbl = " + dbl);System.out.println("value = " + value);double source = 3.1415926;//3.1415926 等价于 31415926e-7double target = 31415926e-7;System.out.println("source =" + source);System.out.println("target =" + target);}}
2.6.2 浮点型的极限以及存储方式
float占据四个字节(Byte),也就是32位(bit),double占据八个字节(Byte),也就是64位,而由于浮点数的存储机制和整数的不一样,Java的浮点数准守IEEE754标准,采用二进制的科学计数法表示浮点数。
对于float,第一位是符号位,接下来八位表示指数,接下来23位表示尾数。
对于double,第一位是符号位,接下来11尾数表示指数,接下来52表示尾数。
float所能表示的最大值会比占据8个字节的long都要大。
package net.ittimeline.java.core.syntax.type;/*** 浮点类型的极限** @author liuguanglei 18601767221@* @create -07-26 15:09* @website * @since JDK11.03*/public class FloatLimitsTest {public static void main(String[] args) {//这里的%e表示整数占位符,会被对应的科学计数法替换System.out.printf("float所能存储的最大值是%e,所能存储的最小值是%e,占据的字节数量是%d \n",Float.MAX_VALUE,Float.MIN_VALUE,Float.BYTES);//使用科学计数法表示float最大值float floatMaxExponent=3.402823e+38f;//使用十进制小数表示float最大值Float floatMax=340282300000000000000000000000000000000f;System.out.println("floatMaxExponent = "+floatMaxExponent);System.out.println("floatMax = "+floatMax);//由于浮点数的存储机制不同占据四个字节的float表示的数据极限比占据八个字节的Long还要大System.out.printf("long所能存储的最大值是%d,所能存储的最小值是%d,占据的字节数量是%d \n",Long.MAX_VALUE,Long.MAX_VALUE,Long.BYTES);System.out.printf("double所能存储的最大值是%e,所能存储的最小值是%e,占据的字节数量是%d \n",Double.MAX_VALUE,Double.MIN_VALUE,Double.BYTES);}}
2.6.3 浮点型精度问题
单精度的float有效位数是7位,即小数点后能保留7位,第八位会进行四舍五入处理。
双精度的double有效位数是16位,即小数点后保留16位,第十七位会进行四舍五入处理。
如果在使用float或者double的小数时,超过了有效位数进行相关的运算时会得到错误的结果。
package net.ittimeline.java.core.syntax.type;import java.math.BigDecimal;/*** 浮点数运算的精度问题** @author liuguanglei 18601767221@* @create -07-26 15:13* @website * @since JDK11.03*/public class FloatPrecisionTest {public static void main(String[] args) {float floatValue=1.123_456_789f;//输出结果是1.1234568 1.123456789,小数点后截取7位,第8位会进行四舍五入System.out.println("floatValue = "+floatValue);double doubleValue=1.111_222_333_444_555_666_777;//输出结果是1.1112223334445557 1.111222333444555666777,小数点后截取16位,第17位会进行四舍五入System.out.println("doubleValue = "+doubleValue);//浮点类型的数据在运算时运算结果会有误差float floatSource=2.0f;//float类型的浮点数小数有效位数是7位float floatTarget=1.2_222_222f;//0.7777778float floatResult=floatSource-floatTarget;System.out.println("float source = "+floatSource);System.out.println("float target = "+floatTarget);//运算结果为 0.7777778 因为float的有效位数是7位System.out.println("float result = "+floatResult);//double类型的 2.0-1.1获取的计算结果是错误的double doubleSource=2.0;double doubleTarget=1.1;double doubleResult=doubleSource-doubleTarget;System.out.println("double source = "+doubleSource);System.out.println("double target = "+doubleTarget);//运算结果为 0.8_999_999_999_999_999 因为double的有效位数是16位System.out.println("double result = "+doubleResult);//如果数据超过了有效位数,在进行相关运算时(例如这里的关系运算:相等)也会得到错误的结果double source=1.000_000_000_000_000_001;double target =1.000_000_000_000_000_002;System.out.println("source == target ? "+(source==target));}}
如果想要获取更高的精度,例如金融计算的业务场景中,会使用到更高精度,此时可以使用java.math包中的BigDecimal来完成。
package net.ittimeline.java.core.syntax.type;import java.math.BigDecimal;/*** 无损精度计算** @author liuguanglei 18601767221@* @create -07-26 16:37* @website * @since JDK11.03*/public class BigDecimalTest {public static void main(String[] args) {//使用字符串构建BigDecimal对象BigDecimal sourceBigDecimal = new BigDecimal("2.0");BigDecimal targetTargetDecimal = new BigDecimal("1.1");//调用BigDecimal提供的subtract方法实现减法运算 解决了浮点数double计算的精度问题BigDecimal resultBigDecimal = sourceBigDecimal.subtract(targetTargetDecimal);System.out.println("resultBigDecimal = " + resultBigDecimal);double source=1.000_000_000_000_000_001;double target =1.000_000_000_000_000_002;BigDecimal doubleSourceBigDecimal=new BigDecimal(source);BigDecimal doubleTargetBigDecimal=new BigDecimal(target);/*** 使用BigDecimal对象提供的equals方法判断是否相等 这里的结果是true* 在使用BigDecimal时不能使用double构造BigDecimal对象,否则会得到错误的结果*/System.out.println("doubleSource equals doubleTarget ? "+doubleSourceBigDecimal.equals(doubleTargetBigDecimal));//将上面的数字转换为字符串之后就能获取精确的运算结果String sourceStr="1.000000000000000001";String targetStr="1.000000000000000002";//只能使用String构造BigDecimal才能获取精确的运算结果BigDecimal strSourceBigDecimal=new BigDecimal(sourceStr);BigDecimal strTargetBigDecimal=new BigDecimal(targetStr);System.out.println("strSource equals strTarget ? "+strSourceBigDecimal.equals(strTargetBigDecimal));}}
2.7 布尔型
布尔类型只有boolean一个类型,而且其取值只能是true或则false。
在日常开中boolean常常和关系、逻辑运算以及条件判断语句一起使用
package net.ittimeline.java.core.syntax.type;import java.util.Random;/*** 布尔型** @author liuguanglei 18601767221@* @create -07-26 10:15* @website * @since JDK11.03*/public class BooleanTest {public static void main(String[] args) {//产生2个0-1之间的随机小数double source=Math.random();double target=Math.random();System.out.println("source = "+source);System.out.println("target = "+target);// >就是关系运算boolean result=source>target;System.out.println("result = "+result);//这里等价于result==trueif(result){System.out.println("source > target");}else{System.out.println("source < target");}}}
2.8 字符型
Java中的字符使用char表示,用于存储单个字符,字符型的变量值使用''
包含。
由于Java使用16位的Unicode作为编码方式,而Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案,因此Java程序支持各种语言的字符。
package net.ittimeline.java.core.syntax.type;/*** 字符型** @author liuguanglei 18601767221@* @create -07-26 10:15* @website * @since JDK11.03*/public class CharacterTest {public static void main(String[] args) {//Java支持Unicode标准,可以存储世界上所有国家的语言文字//英文char english='L';//中文char chinese='刘';//日文char japan='お';//韩文char korean='안';System.out.println("english = "+english);System.out.println("chinese = "+chinese);System.out.println("japan = "+japan);System.out.println("korean = "+korean);}}
除此以外char还能使用Unicode表示字符,由于其占据2个字节空间大小,其表示范围是\u0000 - \uFFFF。在日常开发中还会使用到一些常用的转义字符,例如实现换行的\n以及实现路径转义的\等等。
字符在计算机底层都是以数字的方式存储的,例如字符'a'底层的数字是97,'A'底层的数字是65,字符'0'的底层数字是48,然后依次递增。
package net.ittimeline.java.core.syntax.type;/*** 字符型** @author liuguanglei 18601767221@* @create -07-26 10:15* @website * @since JDK11.03*/public class CharacterTest {public static void main(String[] args) {//Java支持Unicode标准,可以存储世界上所有国家的语言文字//英文char english='L';//中文char chinese='刘';//日文char japan='お';//韩文char korean='안';System.out.println("english = "+english);System.out.println("chinese = "+chinese);System.out.println("japan = "+japan);System.out.println("korean = "+korean);char A='\u0041';//大写字母A对应的数字是65System.out.println("unicode \\u0041 对应的字符是 = "+A);char a='\u0061';//小写的字母a对应的数字是97System.out.println("unicode \\u0061对应的字符是 = "+a);//字符0对应的数字是48char zero='\u0030';System.out.println("unicode \\u002F对应的字符是"+zero);char newLine='\n';System.out.print("hello"+newLine+"world");char path='\\';System.out.println("path = "+path);}}
2.9 类型转换
Java中的八种基本数据类型除了boolean之外,在进行算术运算时,会按照如下的规则进行自动类型转换。
byte,short,char->int->long->float->double
byte和byte运算的结果类型是intbyte和short运算的结果类型是intbyte和char运算的结果类型是intchar和char运算的结果类型是intint和int运算的结果类型是int
即表示范围小的变量会自动提升为表示范围大的变量,这里的表示范围指的是数据类型对应的取值范围,而不是数据类型占用的内存空间(字节数)。
自动类型转换可以在一定程度上避免进行运算的时候结果超过数据类型的极限而造成程序错误。
package net.ittimeline.java.core.syntax.convert;/*** 自动类型转换* 八种基本数据类型除了boolean之外在进行运算时会进行自动类型转换** 在进行运算时,容量小的数据类型的变量与容量大的大数据类型的变量做运算时,结果自动提升为容量大的数据类型,防止运算时超过极限值。* 此时容量大小指的是,数据类型表示数的范围大小,而不是占用内存大小,比如float容量大于long的容量* byte -> short-> int-> long-> float ->double* byte,short,char->int->long->float->double** byte、short、char运算后的结果类型必须是int** @author liuguanglei 18601767221@* @create -07-27 08:53* @website * @since JDK11.03*/public class AutoConvert {public static void main(String[] args) {//byte和int运算的结果是intbyte byteVal=12;int intVal=12;//如果用byte存储运算结果,会编译失败int intResult=byteVal+intVal;System.out.println("intResult = "+intResult);float floatResult=byteVal+intVal;System.out.println("floatResult = "+floatResult);//容量小的变量值自动转换为容量大的变量short shortVal=12;double dblValue=shortVal;char ch='a';int intNewValue=123;int intNewResult=ch+intNewValue;System.out.println("intNewResult = "+intNewResult);byte sourceByte=12;char targetChar=12;int resultInt=sourceByte+targetChar;//short和char运算结果类型是intshort shortNewVal=29;char newChar='b';int result=shortNewVal+newChar;// byte和byte运算也是intbyte source=12;byte target=12;int sourceTarget=source+target;System.out.println("sourceTarget = "+sourceTarget);//intint maxTarget=Integer.MAX_VALUE; int maxSource=Integer.MAX_VALUE;//这里已经超出了int表示的最大值,因此输出的结果是错误的int maxResult=maxSource+maxTarget;System.out.println("maxResult = "+maxResult);}}
而在某些应用场景下我们还可能会使用到强制类型转换,强制类型转换是自动类型转换的逆运算,使用强转符()实现,如果强转的变量超过目标类型的最大值会损失精度。
package net.ittimeline.java.core.syntax.convert;/*** 强制类型转换* 强制类型转换是自动类型转换的逆运算* 需要使用强转符:()* 强制类型转换可能会丢失精度* @author liuguanglei 18601767221@* @create -07-27 09:08* @website * @since JDK11.03*/public class NarrowConvert {public static void main(String[] args) {double dblVal=12.8;System.out.println("dblVal = "+dblVal);//强制类型转换 (强转类型)int intVal=(int)dblVal;//强制转换可能会损失精度System.out.println("intVal = "+intVal);//如果转换的变量值(例如这里的12)在强制转换类型的表示范围之内(例如这里的byte)则不会损失精度long longVal=12;byte byteVal=(byte)longVal;System.out.println("longVal = "+longVal+ " byteVal = "+byteVal);int sourceVal=128;byte targetVal=(byte)sourceVal;//这里会损失精度,因为128已经超过了byte存储的最大值System.out.println("sourceVal = "+sourceVal+ " targetVal = "+targetVal);}}
在给变量赋值时,如果变量的值没有超过变量类型的极限,赋值时也会发送自动类型转换,但是如果赋值时变量的值超过了变量类型的极限,那么此时就需要使用到强制类型转换实现。
package net.ittimeline.java.core.syntax.convert;/*** 类型转换的特殊情况** @author liuguanglei 18601767221@* @create -07-27 09:18* @website * @since JDK11.03*/public class ConvertFeature {public static void main(String[] args) {//整型常量默认为int,浮点常量默认为double//虽然long类型的变量值必须加上L后缀,但是这里的123就是int,这里实际上就是发生了自动类型转换long longSourceVal=123;System.out.println("int max"+Integer.MAX_VALUE);// 因为2147483648超过了int表示的最大值,所以这里必须加上L后缀long bigSource=2147483648L;System.out.println(" bigLongSource = "+bigSource);//3.14默认是double类型,在赋值是必须使用强制类型转换//因为将表示范围大的值赋值给表示范围小的变量必须强制类型转换float fltVal= (float) 3.14;byte byteVal=12;int result=byteVal+1;double dblVal=12.3+byteVal;System.out.println("dblVal = "+dblVal);}}
日常开发中最常用的数据类型是String,它用于存储字符串,使用""
包含起来,String位于java.lang包下,属于引用类型的一种,而且String可以和八种基本数据类型使用加号(+)做连接运算,运算的结果类型是String。
String类型变量的定义以及与char类型的比较。
package net.ittimeline.java.core.syntax.type;/*** 字符串变量** String用于存储字符串(多个字符),属于引用数据类型** @author liuguanglei 18601767221@* @create -07-27 09:22* @website * @since JDK11.03*/public class StringTest {public static void main(String[] args) {//String类型的变量定义 变量值使用""包含String str="Hello World";String charConent="c";System.out.println("charContent = "+charConent);//空字符char emptyC=' ';System.out.println("emptyC= ["+emptyC+"]");//空字符串String emptyStr=" ";System.out.println("emptyStr= ["+emptyStr+"]");}}
String类型与基本数据类型的连接运算,如果加号前后有String类型的变量,那么此时的加号就是表示连接,如果+前后没有String,此时的加号表示算术运算的加法。
package net.ittimeline.java.core.syntax.type;/*** 字符串与八种基本数据类型的运算* 字符串可以和八种基本数据类型进行连接运算,使用+表示连接,运算的结果类型是String类型* +符号前后有String表示连接,没有String表示算术运算的加法** @author liuguanglei 18601767221@* @create -07-27 09:29* @website * @since JDK11.03*/public class StringOperatorTest {public static void main(String[] args) {String info="";int age=18;String name="刘光磊";double height=176.0;boolean isMarry=false;long id=362352355643454356L;info="id : "+id+"姓名:"+name+"\n年龄:"+age+"\n身高:"+height+"\n是否已婚"+isMarry;System.out.println("******个人信息******");System.out.println(info);//判断+表示字符串连接还是算术运算的加法char charVal='a';int number=10;String str="hello";//第一个加法前后都不是String,因此表示加法,而不是字符串连接,而a表示97,第二个加法后面是String,因此表示字符串连接//最后的运算结果是107helloSystem.out.println(charVal+number+str);//第一个加法后面是String,运算结果是ahello10System.out.println(charVal+str+number);//第一个加法后面是String,运算结果是ahello10System.out.println(charVal+(str+number));//第一个加法前后都不是String,因此加号表示算术运算符的加法运算,最后的运算结果是107helloSystem.out.println((charVal+number)+str);String star="*";char tab='\t';String content=star+tab+star;System.out.println("content ="+content);}}
2.10 数据溢出
Java中的每种基本数据类型都有其极限(最大值和最小值),如果在使用的过程中存储的数值超过了极限,则会得到错误的运算结果,在软件开发过程中应该避免此种情况出现。
如果想要搞清楚数据溢出(整数)的原理,需要理解原码、反码和补码,以及整数之间二进制和十进制之间的转换。可以阅读Java架构师成长之道之计算机组成原理计算篇相关的内容。
首先理解数据在计算机底层的表示方法
package net.ittimeline.java.core.syntax.type;/*** 二进制数据表示** @author liuguanglei 18601767221@* @create -07-27 16:41* @website * @since JDK11.03*/public class BinaryTest {public static void main(String[] args) {/*** byte表示范围-128 -127* 原码 最高位是符号位,0表示整数,1表示负数* 正数的原码、反码和补码都一致**/byte byteVal=0b00001110;System.out.println("byteVal = "+byteVal);System.out.println("byteVal to binary String = "+Integer.toBinaryString(byteVal));/*** 14的原码是 00001110 根据原码的规则* // -14的原码是10001110* // 反码是 11110001* // 补码是 11110010 计算机底层使用补码的方式存储数据* //因此输出结果是11110010,将字符串截取八位*///输出结果从右向左截取8位11110010System.out.println(Integer.toBinaryString(-14));//将-14的补码赋值给byteVal变量byteVal=(byte)0b11110010;//因此输出结果是14System.out.println("byteVal = "+byteVal);}}
然后理解溢出的原理
package net.ittimeline.java.core.syntax.type;/*** 数据溢出的原理** @author liuguanglei 18601767221@* @create -07-27 10:42* @website * @since JDK11.03*/public class OverFlowTest {public static void main(String[] args) {//byte 的表示范围 -128 - 127byte maxVal=Byte.MAX_VALUE;//byte所能存储的最大值是127System.out.println("byte所能存储的最大值是"+maxVal);//其在计算机底层的表示为 01111111,因为127是正数//可以使用Integer.toBinaryString()查看原码//由于正数的原码反码和补码都是一样的,因此maxVal在底层的存储就是01111111System.out.println(Integer.toBinaryString(maxVal));/**** 127的原码、反码、补码都是01111111* 而-127的原码是11111111* 反码是10000000* 补码是10000001* 因此输出的结果是10000001*///这里直接使用-127的二进制补码给val变量赋值byte val=(byte)(10000001);System.out.println("val = "+val);System.out.println("-127的补码表示为"+Integer.toBinaryString(val));/*** 整数默认是int类型,这里使用byte进行强制类型转换 11101001 最高位是1表示负数 需要求补码* 11101001 计算补码*先求反码 11101001->10010110*补码 10010110-> 10010111*二进制转十进制 1+2+4+16=23,由于符号位是1因此 最后输出结果是-23***/byte byteBinaryVal=(byte)0b11101001;System.out.println("byteBinaryVal = "+byteBinaryVal );}}