基本知识
[TOC]
java的基本数据类型
java中包含8种基本类型,其中包括4种整型、两种浮点型、一种Unicode编码类型char。
byte,char,short,int,long,float,double,boolean
其中部分类型的字节与所需位数如下
类型 | 字节 | 位数
byte 1 8
short 2 16
int 4 32
long 8 64
float 4 32
double 8 64
其中float和double虽然以单精度和双精度表示浮点数,但是对这两个基本类型对浮点数的表示都不是很
准确,典型的比如这两种基本类型对0.1就无法精确表示,所以如果我们要进行对精度要求很高的计算(如商业上对金钱的计算),则要改用BigDecimal进行计算。
另外,对浮点数的计算也经常会出现下面的三个值:
- 正无穷大
- 负无穷大
- NaN
它们分别对应Float和Double类的POSITIVE_INFINITY、NEGATIVE_INFINITY、NaN常量。而判断一个式子是否等于这三个值,不能出现x==NaN这种判断,而是要使用Float和Double类中对应的isInfinite()、isNaN()方法。
关于char类型,char是用utf-16编码描述的一个代码单元,在utf-16中,由于16位长度只能表示65536个字符,所以就默认映射所有在BMP范围内的字符,这也是我们平时多数使用的char,而有些汉文生僻字在BMP范围之外,如果要表示这些生僻字,就需要使用增补字符,即出现了两个字节表示的char。比如下面的代码:
|
|
其中\uD834\uDF06其实是字符𝌆,由于这个字符不再BMP范围内,\uD834是作为增补字符存在的。
那么运行这个程序的结果如下:
|
|
所以可以看到对这个字符串,字符串中的代码点总数和字符串的长度已经不一样了,字符串长度将增补字符也算作了一个字符,而代码点总数将增补字符和后面的Unicode码算作同一个代码点的两部分,更危险的是如果此时使用charAt方法获取第3第4个字符,则两个都是作为char未识别的?字符。
所以说,char的使用有一定的局限性,如果需要使用char的时候,需要谨慎的考虑。
变量
变量名命名只能用数字、字母、美元符号和下划线,可以使用字母、$、下划线开头,但是惯例是使用字母开头。不能使用数字开头。
字符串
每一个字符串String对象都是不可变的,所以如果出现循环赋值字符串的语句,则使用String会占用很大的资源,效率也会很低,所以java推出了StringBuffer和StringBuilder类。
两个类实现的方法相同,StringBuffer推出的比较早,实现了线程安全,可以被多个线程访问,但是与此对应,它的效率偏低。由于大多数需要进行很多字符串赋值的需求在同一个线程中进行,所以java在1.5中推出StringBuilder类,StringBuilder是线程不安全的,但是效率很高,由于我们一般都是在单线程中进行字符串编辑,所以一般我们都是用StringBuilder类。
输入输出
java的输入通常是使用Scanner类来操作,这里需要注意的是,Scanner获取的输入是可见的,所以不适合进行读取密码的操作。而针对读取密码的需求,java提供了一个专门的Console类,具体使用方法如下:
|
|
可以看到,为了安全性,返回的密码被存储在了char数组中,这也说明了在安全性方面String不如字符数组,在对密码进行处理后,应该马上用一个填充值覆盖数组元素。
但是Console的使用有一些问题,如果直接用集成的编译器idea或者eclipse,使用System.console()方法时会包空指针错误,这是因为idea或者eclipse用的是javaw运行java文件,只要换成dos界面使用javac运行就可以正常使用Console了。
除此之外还有文件的输入输出操作,可以使用之前的Scanner进行读操作:
|
|
使用PrinterWriter进行写操作:
|
|
也可以使用Reader 和Writer接口的一系列子类,这里只举例直接对行进行读取的办法:
|
|
ArrayList
需要注意的是,只有i小于或等于list的大小时,才能调用list.set(i,value),set只是替换原本第i位置的元素,而不是在i位置新增加一个元素,如果需要新添加元素,只能使用add系列方法。
反射
能够分析类能力的程序称为反射。
对反射,首先要了解Class类。在程序运行期间,java运行时系统始终为所有的对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象所属的类,保存这些信息的类被称为Class。
获取Class对象主要有三种方式:
- 实例对象直接调用Object类的getClass方法
- 通过Class.forname(className) 参数传入带完整包名的类名,即可获得该类的Class对象
- 对任意T类型,T.class即可获得该类的Class对象
示例如下:
|
|
对应的输出是:
Example.Reject
Example.Reject
Example.Reject
可见对应输出的都是同一个Class对象。还有一个很有用的方法newInstance(),这是一个Class类的方法,一个类的Class对象可以通过newInstance方法获得该类的实例对象。需要注意的是这个方法调用的是默认的无参数的构造函数来初始化对象,所以如果这个类没有默认的构造函数就会抛出一个异常。
如果需要使用可以传入参数的构造函数,就不要用这个方法来创建实例,而是用Constructor类中的newInstance方法来创建实例。