第7章 Java基础类库
Java 8 提供了4000多个基础类,对于合格的Java程序员,至少要熟悉JavaSE中70%的类。
7.1 与用户互动
7.1.1 运行Java程序的参数
回忆main()方法的签名:
public static void main(String[] args){…}
形参是谁赋值呢? 根据方法调用规则,谁调用谁赋值,JVM调用main()方法,因此JVM为其赋值。
public class ArgsTest { public static void main(String[] args) { System.out.println(args.length); for(String s:args){ System.out.println(s); } } }
直接正常运行,程序输出0.
用 java ArgsTest Java Spring 运行,输出 2 Java Spring
可以看出JVM把后面的字符串赋值给了args数组
@如果字符串包含空格,应该用””括起来 “Java Spring”
7.1.2 使用 Scanner获取键盘输入
Scanner是一个基于正则表达式的文本扫描器,它可以从文件、输入流、字符串中解析出基本类型值和字符串值。
Scanner提供了多个构造器,用于接收文件、输入流、字符串。
Scanner主要提供了两个方法扫描输入。
hasNextXxx() :是否还有下一个输入项。Xxx可以是Int,Long等代表基本数据类型的字符串,如果是判断字符串,可以直接用hasNext()
nextXxx() : 获取下一个输入项。
默认情况下,Scanner使用空白作为多个输入项之间的分隔符。
import java.util.Scanner; public class ScannerKeyBoardTest { public static void main(String[] args) { Scanner sc = new Scanner(System.in); //System.in 代表标准输入,就是键盘输入 //sc.useDelimiter("\n"); //将回车作为分隔符,每次读取一行 while(sc.hasNext()){ System.out.println("键盘输入:"+sc.next()); } } }
为Scanner设置分隔符使用userDelimiter(String pattern),参数为一个正则表达式。
Scanner还提供了两个方法逐行读取输入:
Boolean hasNextLine()
String nextLinr()
Scanner不仅可以读取字符串输入,还可以读入任意基本类型的输入项。
import java.util.Scanner; public class ScannerKeyBoardTest { public static void main(String[] args) { Scanner sc = new Scanner(System.in); //System.in 代表标准输入,就是键盘输入 while(sc.hasNexLongt()){ System.out.println("键盘输入:"+sc.nextLong()); } } }
Scanner还可以读取文件输入,只要在构造器中传入一个File对象
import java.util.Scanner; import java.io.File; public class ScannerFileTest { public static void main(String[] args) throws Exception{ Scanner sc = new Scanner(new File("D://TEST//java//ch7//ScannerFileTest.txt")); //很奇怪就这个文件读取不显示,换成其他的都正常 System.out.println("ScannerFileTest.txt文件内容如下:"); while(sc.hasNextLine()){ System.out.println(sc.nextLine()); } System.out.println("ScannerFileTest.txt文件内容结束:"); } }
注意:1、main方法可能引发Io异常,所以要加上throws Exception
2、建议文件目录为绝对路径,使用相对路径可能出错
3、不知道为什么命令行下打开无法输出文件内容,只输出提示。而且把文件内容删除后,就正常。猜测是编码问题。
7.2 系统相关
获得和平台相关的属性。
7.2.1 System类
System类代表Java程序当前运动的运行平台,程序不能创建System类的对象,
System类提供了一些类成员和类方法,可以直接调用。
import java.io.FileOutputStream; import java.util.Map; import java.util.Properties; public class SystemTest { public static void main(String[] args) throws Exception{ Map<String,String> env = System.getenv(); for(String name : env.keySet()){ System.out.println(name+"--->"+env.get(name)); } System.out.println(System.getenv("JAVA_HOME")); Properties props = System.getProperties(); props.store(new FileOutputStream("props.txt"), "System Properties"); System.out.println(System.getProperty("os.name")); } }
System类还有两个获取当前系统时间的方法:currentTimeMillis()和nanoTime(),
它们返回一个long型整数,值为当前时间和UTC1970年1月1日午夜的时间差,前者以毫秒为单位(常用),后者以纳秒为单位(少用)。
System类的in,out和err 分别代表系统标准输入,标准输出和错误输出流。
并提供setXx()方法来改变。
System类还提供identityHashCode(Object x)方法,返回对象根据地址计算的HashCode值。可以唯一标识对象。
7.2.2 Runtime类
Runtime类代表Java程序的运行时环境。
程序可以通过getRuntime()获取与之关联的Runtime对象。
Runtime也提供了gc()和runFinalization()方法通知系统进行垃圾回收。
提供load(String filename)和loadLibrary(String libname)加载文件和动态链接库。
public class RuntimeTest { public static void main(String[] args) { Runtime rt = Runtime.getRuntime(); System.out.println("处理器数量:"+rt.availableProcessors()); System.out.println("空闲内存:"+rt.freeMemory()); System.out.println("总内存数:"+rt.totalMemory()); System.out.println("可用最大内存数"+rt.maxMemory()); } } Runtime类还有一个功能:单独启动一个进程来运行操作系统的命令: public class ExecTest { public static void main(String[] args) throws Exception{ Runtime rt = Runtime.getRuntime(); rt.exec("notepad.exe"); //运行记事本程序 } }
7.3 常用类
7.3.1 Object类
Object类是所有类、数组、枚举类的父类。
一个类没有指定父类时,默认继承Object类。
bool equals(Object obj):判断两个对象是否为同一个对象
protected void finalize():系统清理垃圾时自动调用
Class<?>getClass():返回对象运行时类
int hashCode():返回对象hashCode值。
String toString(): 返回对象的字符串表示,当print对象或对象和字符串拼接时,会自动调用该方法。
protected clone() :得到当前对象副本。
自定义类实现“克隆”的步骤:
1、实现Cloneable接口
2、实现clone()方法 ,实现clone()方法时调用super.clone(),并返回该对象。
例:
class User implements Cloneable { int age; public User(int age) { this.age = age; } public User clone() throws CloneNotSupportedException { return (User) super.clone(); }
Object提供的Clone机制只是简单复制,如果实例变量是引用类型,则直接复制引用变量,而不是复制出一个对象。
7.3.2 Java 7 新增的Objects类
Object提供的Clone机制只是简单复制,如果实例变量是引用类型,则直接复制引用变量,而不是复制出一个对象。
@工具类命名习惯是添加一个s,如Arrays。
7.3.3 String、StringBuffer和StringBuilder类
字符串是一串字符,Java提供String和StringBuffer来封装字符串。
String类是不可变类,里面字符不可改变。
StringBuffer是字符序列可变的字符串,可以使用append,insert, reverse setCharAt,setLength等方法来改变字符串。
使用toString()可以将其转换成String对象。
JDK1.5新增StringBuilder类,与StringBuffer类相似,只是StringBuild没有实现线程安全功能,性能略高。
通常考虑StringBuilder实现可变字符串。
String提供大量构造器来创建String对象。
String也提供了大量方法来操作字符串对象。
//这些方法都可以在API文档中查阅。
char CharAt(int index) :返回index位置的字符
int compareTo(Stirng s) :比较字符串大小,相等返回0,不相等时,返回第一个不等的字符差。当较长字符串正好前面是较短字符串,则返回长度差。
String concat(String srt) 拼接,功能同+
Boolean contentEquals(StringBuffer sb) :和StringBuffer对象比较
static String copyValueOf(char[]data) 将字符数组连成字符串。
static String copyValueOf(char[]data,int offset,int count) :连接数组中的部分元素
Boolean endsWith(String suffix): 是否以suffix结尾
Boolean equals(Object obj) 是否和对象字符序列相等
Boolean equalsIgnoreCase(Object obj) 是否和对象字符序列相等,忽略大小写
byte[] getBytes(): 转换成byte数组
void getChars(int srcBegin,int srcEnd, char[] dst,int dstBegin):复制到dst数组中
int indexOf(int ch) :ch第一次出现的位置
int indexOf(int ch,int fromIndex)
int indexOf(String str):str子串第一次出现的位置
int indexOf(String str, int fromIndex)
int lastIndexOf(int ch) :ch最后一次出现
int lastIndexOf(int ch,int fromIndex)
int lastIndexOf(String str)
int lastIndexOf(String str,int fromIndex)
int length()
String replace(char oldc,char newc); 将第一个oldc替换成newc
Boolean startsWith(String prefix) 是否以prefix开始
Boolean startsWith(String prefix,int toffset)
String substring(int beginIndex): 子串
String substring(int beginIndex, int endIndex)
char[] toCharrArray()
String toLowerCase()
String toUpperCase()
static String valueOf(X x), 将基本类型转换成字符串。
7.3.4 Math类
7.3.5 Java7 的ThreadLocalRandom和 Random
Random类用于生成一个伪随机数,它有两个构造器,一个使用默认的时间种子,另一个需要传入一个long型整数作为种子。
ThreadLocalRandom是Random的加强版,具有更好的线程安全性。
Random 用法
Random rand = new Random();
rand.nextXxx();
推荐使用当前时间作为Random种子
Random rand = new Random(System.currentTimeMillis());
ThreadLocalRandom用法和Random类似
ThreadLocalRandom rand = ThreadLocalRandom.current();
rand.nextXxx();
7.3.6 BigDecimal类
精确表示浮点数:
创建对象
BigDecimal(String val)
运算:提供了add() subtract() multiply() divide() pow()等方法
如果程序中要对浮点数进行运算,可以考虑将double保证成BigDecimal对象,然后进计算,再讲结果转换成double。
可以以BigDecimal为基础定义一个Arith工具类。
public class Arith { private static final int DEF_DIV_SCALE = 10;//除法精度 private Arith(){} //私有构造器,使这个类不能实例化 public static double add(double v1, double v2) { BigDecimal b1 = BigDecimal.valueOf(v1); BigDecimal b2 = BigDecimal.valueOf(v2); return b1.add(b2).doubleValue(); } //剩下的三种类似,略 //除法使用的方法 // b1.divide(b2,DEF_DIV_SCALE,BigDecimal.ROUND_HALF_UP).doubleValue(); }
7.4 java8的日期、时间类
7.4.1 Date类
Date类从JDK1.0就存在了,大部分构造器方法都已经过时,不推荐使用了(Deprecated)
还可以使用的构造器:
Date() :返回当前时间
Date(long date):根据date生成Date对象,该参数data表示与GMT1970.1.1 00:00:00的时间差,单位毫秒
剩下的方法:
Boolean after(Date when)
Boolean before(Date when)
long getTime() 返回时间差
void setTime(long time) 设置时间
@总体上,Date是一个糟糕的类,不不推荐使用
7.4.2 Calendar类
Calender类用来取代Date类来处理日期和时间。
Calendar是一个抽象类,用来表示日历。
Java提供了一个GregorianCalendar类,代表公历。
Calendar是抽象类,不能根据根据构造器创建Calendar对象,但可以通过getInstance()方法获取Calendar对象。
Calendar calendar = Calendar.getInstance();
Calendar提供大量访问、修改日期的方法
void add(int field, int amount)
int get(int field);
void set(int field,int value)
void set(int year,int month,int date)
void set(int year,int month,int date,int h,int m, int s);
field是Calendar的类变量,如Calendar.YEAR,需要注意的是
Calendar.MONTH 月份的起始值是0.
设置Calendar的兼容性
setLenient(true|false) ,默认为true,具有容错性,当参数异常是不会报错。
false时参数异常会报错。
3、set()方法延迟修改
,Calendar代表的时间在下次调用get()等方法时才会重新计算。
7.4.3Java8 新增的日期、时间包
java.time
该包包含了如下的类:
Clock: 获取指定时区的当前日期时间
Duration:持续时间
Instant:某个具体的时刻
LocalDate:不带时区的日期
LocalTime: 不带时区的时间
LocalDateTime:日期、时间
MonthDay:月日
Year:年
ZonedDateTime:时区化的日期时间
ZonedID:时区
7.5 正则表达式
字符串处理工具,用于对字符串进行查找,提取,分割,替换等。
String类提供了关于正则的方法:
Boolean matches(String regex):判断字符串是否匹配正则表达式
String replaceAll(String regex, String replacement) 将所有匹配regex的子串 替换成replacement
Strubg replaceFirst(String regex, String replacement);
String[] split(String regex) :以regex作为分隔符
7.5.1 创建正则表达式
正则表达式是一个用于匹配字符串的模板。
正则表达式本身就是一个特殊的字符串。
正则表达式支持的字符:
字符 解释
x 字符x(合法的任何字符)
\t 制表
\n 换行
\r 回车
…
特殊字符:
特殊字符 说明
$ 匹配一行的结尾。
^ 匹配一行的开头
() 标记子表达式的开始和结束
[] 确定中括号表达式的开始和结束
{} 标记前面子表达式出现频度
* 前面子表达式可以出现任意次
+ 前面子表达式可以出现一次或多次
? 前面子表达式可以出现0次或1次
. 匹配出换行以外的单字符
\ 转义字符
| 两项中任选一项
通配符:可以匹配多个字节的特殊字符
字符 说明
. 匹配任何字符
\d 0~9的数字
\D 非数字
\s 空白
\S 非空白
\w 单词(包括0-9,26个字母,下划线)
\W 非单词
方括号表达式
表示枚举 如[abc],表示a,b,c任意一个字符
表示范围- 如[a-f],表示a-f范围任意一个字符,范围可枚举可以结合使用:[a-cx-z]表示a-c,x-z内的任意字符
表示求否^ 如[^abc] 表示非abc的字符,[^a-f]不在a-f范围内的字符
表示与 && 如[a-z&&[def]] a-z和[def]的交集 def
表示并 类似枚举,如[a-d[m-p]] 等价[a-dm-p]
圆括号表达式
将多个表达式组合成一个,可以使用|符号。例如”((public)|(protected)|(private))” 可以匹配访问控制符的任意一个。
边界匹配符
^ 开头
$ 结尾
\b 单词边界
\B
\A 输入开头
\G 前一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符
\z 输入的结尾
数量标识符
数量标识符3中模式:
1、Greedy模式:默认采用贪婪模式。贪婪模式的表达式会一直匹配下去,直到无法匹配。
2、Relunctant(勉强)模式:用?后缀表示,匹配最少的字符,也称最小匹配模式。
3、Possessive(占有)模式:用+后缀表示。
7.5.2 使用正则表达式
Pattern 对象是正则表达式编译后在内存中的表示形式,正则表达式必须先编译为Pattern对象,再使用Pattern对象创建Match对象。
Pattern p = Pattern.compile(“a*b)”);
Match m = p.matcher(“aaaaab”);
Boolean b = m.matches();
//前两步可以合并
Match m = Pattern.compile(“(a*b)”).matcher(“aaaab”);
find():返回是否包含匹配的子串
group():返回上一次匹配的子串。
Matcher类提供的find()方法和group()方法可以从目标字符串中一次提取特定子串。
比如提取电话号码:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class FindGroup { public static void main(String[] args) { String str = "求购一本<java>,有意者联系13500006666"+"交朋友,电话号码13611112222" +"出售二手书,有意联系15822223333"; //创建Pattern对象,匹配13x,15x的手机号,并用其建立Matcher对象。 Matcher m = Pattern.compile("((13\\d)|(15\\d))\\d{8}").matcher(str); while(m.find()){ System.out.println(m.group()); } } }
@由于Java字符串本身反斜杠需要转义,所以两个\\相当于一个\。
()
{}
+ * . |
7.6 国际化与格式化
全球化意味着同一版本能适应不同地区的市场。
7.6.1 Java国际化的思路
将程序中的标签,提示灯信息放在资源文件中,程序需要支持哪些国家,语言,就提供对应的资源文件。
资源文件是key-value对,每个资源文件的key是不变的,value随国家语言改变。
Java程序的国际化主要通过3个类完成:
java.util.ResourceBundle:用于加载资源包
java.util.Locale :用于封装特定的国家/区域,语言环境
java.text.MessageFormat: 用于格式化带占位符的字符串。
为了实现程序的国际化,首先要提供需要的资源文件(key-value对)。
资源文件的key 是程序使用的部分,value是程序界面显示的字符串。
资源文件的命名有如下三种形式:
baseName_language_country.properties
baseName_language.properties
baseName.properties
baseName是资源文件的基本名,由用户指定,language和country不可随意变化,必须是Java支持的语言和国家。
7.6.2 Java支持的国家和语言
通过Locale 类提供的方法可以得到Java支持的国家和语言:
import java.util.Locale; public class LocalList { public static void main(String[] args) { Locale[] localList = Locale.getAvailableLocales(); for(Locale l:localList){ System.out.println(l.getDisplayCountry()+ "="+l.getCountry()+" " +l.getDisplayLanguage() +"="+l.getLanguage()); } } }
7.6.3 完成程序国际化
1、有如下程序
public class RawHello { public static void main(String[] args) { System.out.println("Hello,world");} }
直接执行永远只是输出Hello,world
为了让输出的字符串随运行环境改变,应该将要输出的字符串定义在资源包里。
为上面程序提供两个文件:
mess.properties
内容:
hello=你好!
mess_en_US.properties
内容:
hello=Welcome You!
//JDK9 以上支持UTF-8 ,不需要这个转换了。
对于包含非西欧字符的资源文件,Java提供native2ascii工具处理,语法:native2ascii 原资源文件 目的资源文件
在命令行输入:native2ascii mess.properties mess_zh_CN.properties
所以我这里直接将前面的mess.properties改名为mess_zh_CN.properties
import java.util.Locale; import java.util.ResourceBundle; public class Hello { public static void main(String[] args) { //获得系统默认的国家/语言 Locale myLocale = Locale.getDefault(Locale.Category.FORMAT); ResourceBundle bundle = ResourceBundle.getBundle("mess",myLocale); System.out.println(bundle.getString("hello")); } }
然后运行,报错
java.util.MissingResourceException: Can't find bundle for base name mess, locale zh_CN
//后来发现还是编码问题
全都用UTF-8编码,文件放在和Hello.java同一目录下。
编译时javac -encoding UTF-8 Hello.java,再执行就可以了。
7.6.4 使用MessageFormat 处理包含占位符的字符串
MessageFormat包含一个静态方法format(String pattern,Object…values); 返回后面多个参数值填充前面的pattern字符串。pattern是一个带占位符的字符串。
msg = Hello,{0}! Today is {1}/
System.out.println(MessageFormat.format(msg,”yeeku”,new Date());
7.6.5 使用类文件代替资源文件
允许使用类文件代替资源文件。
类名必须是 baseName_language_country
必须继承ListResourceBundle,并重写getContents(),该方法返回Object数组,数组每一项都是key-value对。
import java.util.ListResourceBundle; public class myMess_zh_CN extends ListResourceBundle { private final Object myData[][]= { {"msg","{0},你好!今天的日期是{1}"} }; public Object[][] getContents() { return myData; } }
可以替换对应的properties文件。
7.6.6 使用NumberFormat 格式化数字
NumberFormat 和 DateFormat 都包含format()和parse()方法。
用于格式化为字符串和解析字符串。
NumberFormat是一个抽象基类,它提供了几个类方法来获得NumberFormat对象。
getCuttencyInstance():返回默认Locale的货币各时期
getIntergerInstance()
getNumberInstance()
getPercentInstance()
获得NumberFormat对象后,就可以使用它的format()方法格式化数值。
7.6.7 使用DateFormat格式化日期、时间
getDateInstance()
getTimeInstance()
getDateTimeInstance()
7.6.8 使用SimpleDateFormat 格式化日期
SimpleDateFormat可以灵活地格式化Date,也可以用于解析各种格式的日期字符串。创建时传入一个pattern日期模板字符串。
7.7 Java8新增的日期、时间格式器
7.7.1 使用DateTimeFormat 完成格式化
7.7.2 使用DateTimeFormat 解析字符串