第七章Java基础类库

Java学习笔记

第七章——Java基础类库

与用户互动

运行Java程序的参数

  • mian函数的方法签名如下:
public static void main(String[] args){...}
  • public:Java类由JVM调用,为了让JVM可以自由调用这个方法,使用该修饰符把它暴露出来
  • static:JVM调用这个主方法时,不会创建对象,而是通过该类来调用,故使用static
  • void:将该方法的返回值返回给JVM没有任何意义
  • String[] args形参:在调用该程序时,在主类名称后增加其他字符串,会被当做形参处理,如果没有,则是长度为0的数组:
public class ArgsTest {    public static void main(String[] args)    {        System.out.println(args.length);        for (String tmp: args             ) {            System.out.println(tmp);        }    }}
  • 使用如下命令来运行上面程序:
lancibe@lancibe-TM1701:~/java/test/src/chapter7$ java ArgsTest Lancibe1Lancibe

使用Scanner获取键盘输入

  • 运行Java程序时传入参数只能在开始运行之前就设定几个固定的参数。对于更复杂的情形,程序需要在运行过程中获取输入。
  • 使用Scanner类可以很方便的获取用户的键盘输入,Scanner是一个基于正则表达式的文本扫描器,他可以从文件、输入流、字符串中解析出基本类型值和字符串值。Scanner类提供了多个构造器,不同的构造器可以接受文件、输入流、字符串作为数据源,用于从文件、输入流、字符串中解析数据。
  • Scanner主要提供了两个方法来扫描输入。
    • hasNextXxx():是否还有下一个输入项,如果只是判断是否包含下一个字符串,则可以使用hasNext()
    • nextXxx():获取下一个输入项。Xxx的含义与前一个方法中的Xxx相同。
import java.util.Scanner;public class ScannerKeyBoardTest {    public static void main(String[] args)    {        Scanner sc = new Scanner(System.in);        while(sc.hasNext())        {            System.out.println("键盘输入的内容是:" sc.next());        }    }}
  • 为Scanner设置分隔符时使用useDelimiter(String pattern)方法即可,该方法的参数应该是一个正则表达式,关于正则表达式的内容会在后面介绍。只要把上面程序中粗体字代码行的注释去掉,程序就会把键盘的每行输入当成一个输入项,不会以空格、Tab制表符等作为分隔符
  • 事实上,Scanner提供了两个简单的方法来逐行读取
    • boolean hasNextLine():返回输入源中是否还有下面一行
    • String nextLine():返回输入源中下一行的字符串
  • Scanner不仅能读取用户的键盘输入,还可以读取文件输入。只要在创建Scanner对象时传入File对象作为参数,就可以让Scanner读取该文件的内容。
import java.io.File;import java.util.Scanner;public class ScannerFileTest {    public static void main(String[] args)            throws Exception    {        Scanner sc = new Scanner(new File("ScannerFileTest.java"));        System.out.println("ScannerFileTest.java文件内容如下:");        while(sc.hasNextLine())        {            System.out.println(sc.nextLine());        }    }}
  • 上面程序创建Scanner对象时传入了一个File对象作为参数,这表明该程序会自动读取ScannerFileTest.java文件中的内容。上面程序使用了hasNextLine()和nextLine()两个方法来读取文件内容,这表明该程序将逐行读取ScannerFileTest.java文件的内容。

系统相关

System类

  • System类代表当前Java程序的运行平台,程序不能创建System类的对象,该类提供了一些类变量和类方法,允许直接通过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.txt文件中        props.store(new FileOutputStream("props.txt"), "System Properties");        //输出特定的系统属性        System.out.println(System.getProperty("os.name"));    }}
  • 上面程序通过调用System类的getenv()、getProperties()、getProperty()等方法来访问程序的环境变量和系统属性,程序运行的结果会输出操作系统所有的环境变量值,并输出JAVA_HOME环境变量,以及os.name系统属性的值。
  • System类的in、out和err分别代表系统的标准输入(键盘)标准输出(显示器)和错误输出流,并提供了setIn()、setOut()、setErr()方法来改变系统的标准输入输出以及错误流。
  • System类还提供了一个identityHashCode(Object x)方法,该方法返回指定对象的精确hashCode值,也就是根据该对象的地址计算得到的hashCode值。当某个类的hashCode()方法被重写后,该类实例的hashCode()方法就不能唯一的标识该对象;但通过该方法刚回的hashCode值依然是根据该对象的地址计算而来的。所以,如果两个对象的identityHashCode值相同,则两个对象绝对是同一个对象
public class IdentityHashCodeTest {    public static void main(String[] args)    {        //下面程序中s1和s2是两个不同的对象        String s1 = new String("Hello");        String s2 = new String("Hello");        //String重写了hashCode()方法——改为根据字符序列来计算hashCode值        //因为s1和s2的字符序列相同,所以他们的identityHashCode值不同        System.out.println(s1.hashCode() "----" System.identityHashCode(s2));        String s3 = "lancibe";        String s4 = "lancibe";        //s3和s4是相同的字符串对象,所以他们的identityHashCode值相同        System.out.println(System.identityHashCode(s3)   "----"   System.identityHashCode(s4));    }}
  • 运行结果如下:
lancibe@lancibe-TM1701:~/java/test/src/chapter7$ java IdentityHashCodeTest 69609650----225534817664740647----664740647

Runtime类与Java9 的ProcessHandle

  • Runtime类代表Java程序的运行环境,每个Java程序都有一个与之对应的Runtime实例,应用程序通过该对象与其运行时的环境相连。应用程序不能创建自己的Runtime实例,但可以通过getRuntime()方法获取与之相关联的Runtime对象。
  • 与System类似的是,该类也提供了gc()方法和runFinalization()方法来通知系统进行垃圾回收、清理系统资源,并提供了load(String filename)和loadLibrary(String libname)方法来加载文件和动态链接库。
  • Runtime类代表了Java程序的运行时环境,可以访问JVM相关信息,如处理器数量、内存信息等。
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());    }}
  • 其结果如下:
处理器数量:8空闲内存数:261958072总内存数:264241152可用最大内存数:4169138176
  • 上面程序中使用的就是Runtime类提供的访问JVM相关信息的方法。除此之外,Runtime类还有一个功能——可以直接单独启动一个进程来运行操作系统的命令:
public class ExecTest {    public static void main(String[] args)            throws Exception    {        Runtime rt = Runtime.getRuntime();        rt.exec("gnome-terminal");    }}
  • 将启动一个新的终端,这样的操作是十分有用而且有趣的。通过exec启动平台上的命令之后,通过该接口可以获取进城的ID、父进程和后代进程;通过该接口的onExit()方法可以在进城结束时完成某些行为。
  • ProcessHandle还提供了一个ProcessHandle.Info类,用于获取进程的命令、参数、启动时间、累计运行时间、用户等信息。
import java.util.concurrent.CompletableFuture;public class ProcessHandleTest {    public static void main(String[] args)            throws Exception    {        Runtime rt = Runtime.getRuntime();        Process p = rt.exec("gnome-terminal");        ProcessHandle ph = p.toHandle();        System.out.println("进程是否运行:" ph.isAlive());        System.out.println("进程ID:" ph.pid());        System.out.println("父进程:" ph.parent());        //获取ProcessHandle.Info信息        ProcessHandle.Info info = ph.info();        //通过ProcessHandle.Info信息获取进程相关信息        System.out.println("进程命令:" info.command());        System.out.println("进程参数:" info.arguments());        System.out.println("进程启动时间:" info.startInstant());        System.out.println("进程累计运行时间:" info.totalCpuDuration());        //通过CompletableFuture在进程结束时运行某个任务        CompletableFuture<ProcessHandle> cf = ph.onExit();        cf.thenRunAsync( () -> {            System.out.println("程序退出");        });        Thread.sleep(5000);    }}

常用类

Object类

  • Object类是所有类、数组、枚举类的父类,也就是说,Java允许把任何类型的对象赋给Object类型的变量。当定义一个类时没有使用extends关键字为她显式指定父类,则该类默认继承Object类。
  • 该类提供了如下几个常用方法:
boolean equals(Object obj);//判断指定对象与该对象是否相等。protected void finalize();//当系统中没有引用变量应用到该对象时,垃圾回收器调用此方法来回收该对象的资源Class<?> getClass();//返回该对象的运行时类int hashCode();//返回该对象的hashCode值String toString();//返回该对象的字符串表示
  • Java还提供了一个protected修饰的clone()方法,该方法用于帮助其他对象来实现自我克隆,所谓的自我克隆就是得到一个当前对象的副本,而且二者之间完全隔离。由于Object类提供的clone()方法使用了protected修饰,因此该方法只能被子类重写或调用。
  • 自定义类实现“克隆”的步骤如下:
  1. 自定义类实现Cloneable接口。这是一个标志性的接口,实现该接口的对象可以实现“自我克隆”,接口里没有定义任何方法。
  2. 自定义类实现自己的clone()方法
  3. 实现clone()方法时通过super.clone()调用Object实现的clone()方法来得到该对象的副本,并返回该副本。
class Address{    String detail;    public Address(String detail)    {        this.detail = detail;    }}class User implements Cloneable{    int age;    Address address;    public User(int age)    {        this.age = age;        address = new Address("西长安街");    }    //通过super.clone()来实现clone()方法    public User clone()        throws CloneNotSupportedException    {        return (User)super.clone();    }}public class CloneTest {    public static void main(String[] args)        throws CloneNotSupportedException    {        User u1 = new User(29);        User u2 = u1.clone();        System.out.println(u1 == u2);        System.out.println(u1.address == u2.address);    }}

Java7 新增的Objects类

  • Java7 新增了一个Objects工具类,他提供了一些工具方法来操作对象,这些工具方法大多是“空指针”安全的。比如不能确定一个引用变量是否为null,如果贸然地调用该变量的toString方法,可能会引发NullPointerException异常;但如果使用Objects类提供的toString(Object o)方法,就不会引发了,当o是null时,程序将返回一个“null”字符串。
import java.util.Objects;public class ObjectsTest {    static ObjectsTest obj;    public static void main(String[] args)    {        System.out.println(Objects.hashCode(obj));        System.out.println(Objects.toString(obj));        //obj不能为null,如果obj为null则引发异常        System.out.println(Objects.requireNonNull(obj, "obj参数不能是null"));    }}

Java9 改进的String、StringBuffer、StringBuilder类

  • 字符串就是一连串的字符序列,Java提供了String、StringBuffer、StringBuilder三个类来封装字符串,并提供了一系列方法来操作字符串对象。
  • String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
  • StringBuffer对象这代表一个字符序列可变的字符串,当一个StringBuffer被创建后,通过他提供的append()、insert()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。
  • StringBuilder类也代表可变字符串对象。实际上,这两个类基本相似,两个类的构造器和方法也基本相同。不同的是,前者是线程安全的,迩后者并没有实现线程安全功能,所以性能略高。因此在通常情况下,应优先考虑StringBuilder类

这三个类都实现了CharSequence接口,因此该接口可以认为是一个字符串的协议接口。

  • String类提供了大量构造器来创建String对象:
String():创建一个包含0个字符串序列的String对象String(byte[] bytes, Charset charset):使用指定的字符集将指定的byte[]数组解码成一个新的String对象。String(byte[] bytes, int offset, int length):使用平台的默认字符集将指定的byte[]数组从offset开始、长度为length的子数组解码成一个新的String对象。String(byte[] bytes, int offset, int length, String charsetName):使用指定的字符集将指定的bytes[]数组从offset开始、长度为length的子数组解码成一个新的String对象。...

Math类

  • Java通过该类来完成复杂的数学运算
public class MathTest {    public static void main(String[] args)    {        //三角运算        //弧度转角度        System.out.println("Math.toDegrees(1.57):" Math.toDegrees(1.57));        //将角度转换为弧度        System.out.println("Math.toRadians(90):" Math.toRadians(90));        //计算反余弦,返回的角度范围在0.0到pi之间        System.out.println("Math.acos(1.2):" Math.acos(1.2));        //计算双曲正弦        System.out.println("Math.sinh(1.2):" Math.sinh(1.2));        //将矩形坐标(x,y)转换为极坐标(r, thet)        System.out.println("Math.atan2(0.1, 0.2):" Math.atan2(0.1, 0.2))        //取整运算        //向下取整        System.out.println("Math.floor(-1.2):" Math.floor(-1.2));        //向上取整        System.out.println("Math.ceil(2.3):" Math.ceil(2.3));        //四舍五入        System.out.println("Math.round(3.4):" Math.round(3.4));        //下面是乘方开方指数运算        //欧拉数的e的n次幂        System.out.println("Math.exp(2):" Math.exp(2));    }}

正则表达式

  • 正则表达式是一个强大的字符串处理工具,可以对字符串进行查找、提取、分割、替换等操作。String类里也提供了如下几个特殊的方法:
boolean matches(String regex);//判断字符串是否匹配指定的正则表达式String replaceAll(String regex, String replacement);//将该字符串中所有匹配regex的子串替换成replacementString replaceFirst(String regex, String replacement);//将该字符串中第一个匹配regex的子串替换为replacementString[] split(String regex);//以regex作为分隔符,把该字符串分割成多个子串。
  • 上面这些特殊的方法都依赖于Java提供的正则表达式支持,除此之外,Java还提供了Pattern和Matcher这两个类专门用于提供正则表达式支持。

创建正则表达式

  • 正则表达式就是一个用于匹配字符串的模板,可以匹配一批字符串,所以创建正则表达式就是创建一个特殊的字符串。下表时正则表达式所支持的合法字符
字符 解释
x 字符x
\0mnn 八进制数0mnn所表示的字符
\xhh 十六进制0xhh
\uhhhh 十六进制0xhhhh所表示的Unicode字符
\t 制表符
\n 换行符
\r 回车符
\f 换页符
\a 报警(bell)符
\e Escape符
\cx x对应的控制符,例如\cM代表Ctrl-M。x的值必须为AZ或az之一
  • 除此之外,正则表达式中还有一些特殊的字符,这些特殊字符在正则表达式中有其特殊的用途,如果需要匹配这些字符,则必须首先将这些字符转义,也就是加上反斜线。
特殊字符 说明
$ 匹配一行的结尾
^ 匹配一行的开头
() 标记子表达式的开始和结束位置
[] 用于确定中括号表达式的开始和结束位置
{} 用于标记前面子表达式的出现频度
* 指定前面子表达式可以出现零次或多次
指定前面子表达式可以出现一次或多次
? 指定前面子表达式可以出现零次或一次
. 匹配除换行符\n以外的任何单字符
\ 用于转义下一个字符,或指定八进制、十六进制字符
| 制定两项之间任选一项
  • 注意如果需要匹配特殊字符本身,应使用\\m的形式。
  • 下面举例说明具体使用方法
"\u0041\\\\" //匹配A"\u0061\t" //匹配a<制表符>"\\?\\[" //匹配?[
  • 前面的正则表达式依然只能匹配单个字符,这因为还未在正则表达式中使用“通配符”,他是可以匹配多个字符串的特殊字符。正则表达式中的“通配符”远远超出了普通通配符的功能,他被称为预定义字符
预定义字符 说明
. 可以匹配任何字符
\d 匹配0~9之间的所有数字
\D 匹配非数字
\s 匹配所有的空白字符,包括空格、制表符、回车符、换行符、换页符等
\S 匹配所有的非空白字符
\w 匹配所有的单词字符,包括0~9所有数字、26个英文字母和下划线_
\W 匹配所有的非单词字符
  • 以此可以创建更加强大的正则表达式:
"c\\wt" //可以匹配cat、cbt、c9t等一批字符串"\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d" //匹配一个型如000-000-0000形式的电话号码
  • 在一些特殊的情况下,例如只想匹配a~f之间的字母,或者匹配除ab以外的所有小写字母,或者匹配中文字符,上面的预定义字符就无能为力了,此时就需要方括号表达式。
方括号表达式 说明
表示枚举 例如[abc],表示a、b、c其中任意一个字符
表示范围:- 例如[a-f],表示af之间的任意字符,[\\u0041-\\u0056]表示十六进制的这两个字符之间的字符,[a-cx-z]表示ac或x~z范围内的任意字符
表示求否:^ 例如[abc]表示非a、b、c的任意字符,[a-f]表示非a~f之间的任意字符
表示“与”运算:&& 例如[a-z&&[def]],求a~z和[def]的交集,表示d、e或f。[a-z&&[^m-p]],即[a-lq-z]。
表示“并”运算 并运算与前面的枚举类似,如[a-d[m-p]]表示[a-dm-p]。
  • 正则表达式还支持圆括号表达式,用于将多个表达式组成一个子表达式,圆括号中可以使用或运算符(|)。例如,正则表达式`"((public)|(protected)|(private))"用于匹配Java的三个访问控制符其中之一。
  • 除此之外,Java正则表达式还支持如下表所示的几个边界控制符
边界匹配符 说明
^ 行的开头
$ 行的结尾
\b 单词的边界
\B 非单词的边界
\A 输入的开头
\G 前一个匹配的结尾
\Z 输入的结尾,仅用于最后的控制符
\z 输入的结尾
  • 正则表达式还提供了数量标识符:

    • Greedy(贪婪模式):数量表示符默认采用贪婪模式,除非另有表示。该模式的表达式会一直匹配下去,直至无法匹配。如果发现表达式匹配的结果与预期不符,很有可能是因为使用了贪婪模式
    • Reluctant(勉强模式):用问号后缀(?)表示,他只会匹配最小的字符,也称为最小匹配模式
    • Possessive(占有模式):用加号后缀( )表示,目前只有Java支持占有模式,通常比较少用。
贪婪模式 勉强模式 占有模式 说明
X? X?? X? X表达式出现零次或一次
X* X*? X* X表达式出现零次或多次
X X ? X X表达式出现一次或多次
X{n} X{n}? X{n} X表达式出现n次
X{n,} X{n,}? X{n,} X表达式至少出现n次
X{n,m} X{n,m}? X{n,m} X表达式最少出现n次,最多出现m次
  • 关于贪婪模式和勉强模式的对比,可以看如下代码:
public class test {    public static void main(String[] args)    {        String str = "hello, java";        System.out.println(str.replaceFirst("\\w*", "lancibe"));        System.out.println(str.replaceFirst("\\w*?", "lancibe"));    }}
  • 运行结果如下:
lancibe@lancibe-TM1701:~/java/test/src/chapter7$ java test lancibe, javalancibehello, java
  • 当从"hello, java"字符串中查找匹配"\w*"子串时,第一行使用了贪婪模式,所以直到逗号才停止,第二行使用了勉强模式,故匹配了0个字符。

使用正则表达式

  • 一旦在程序中定义了正则表达式,就可以通过Pattern和Matcher来使用正则表达式。
  • Pattern对象是正则表达式编译后在内存中的表示形式,因此,正则表达式字符串必须先被编译为Pattern对象,然后再利用该Pattern对象创建对应的Matcher对象,执行匹配所涉及的状态保留在Matcher对象中,多个Matcher对象可以共享同一个Pattern对象。
  • 因此,典型的调用顺序如下:
//将一个字符串编译成Pattern对象Pattern p = Pattern.compile("a*b");//使用Pattern对象创建Matcher对象Matcher m = p.matcher("aaaaab");boolean b = m.matches(); //返回true
  • 上面定义的Pattern对象可以多次重复使用。如果一个正则表达式仅需要一次使用,这可以直接使用Pattern类的静态matches()方法,此方法自动把指定字符串编译成匿名的Pattern对象,并执行匹配:
boolean p = Pattern.matches("a*b", "aaaaab");//返回true
  • Pattern是不可变类,可供多个并发线程安全使用
  • Matcher类提供了如下多个常用方法
find():返回目标字符串中是否包含与Pattern匹配的子串group():返回上一次与Pattern匹配的子串start():返回上一次与Pattern匹配的子串在目标字符串中的开始位置end():返回上一次与Pattern匹配的子串在目标字符串中的结束位置加一lookingAt():放回目标字符串前面部分与Pattern是否匹配matches():返回整个目标字符串与Pattern是否匹配reset():将现有的Matcher对象应用于一个新的字符序列。

-下面程序示范了如何从大段字符串中找出电话号码。

import java.util.regex.Matcher;import java.util.regex.Pattern;public class FindGroup {    public static void main(String[] args)    {        //使用字符串模拟从网络上得到的网页源码        String str = "Lancibe, tel:13512341234"                  "Lan, tel:15701234567"                  "Xun, tel:14288886666";        Matcher m = Pattern.compile("((13\\d)|(15\\d)|(14\\d))\\d{8}").matcher(str);        while(m.find())        {            System.out.println(m.group());        }    }}
  • find()方法还可以传入一个int类型的参数,带int参数的find()方法将从该int索引处向下搜索。
  • start()和end()方法主要用于确定子串在目标字符串中的位置:
import java.util.regex.Matcher;import java.util.regex.Pattern;public class StartEnd {    public static void main(String[] args)    {        String regStr = "Java is not very easy :(";        System.out.println("目标字符串:" regStr);        Matcher m = Pattern.compile("\\w ").matcher(regStr);        while(m.find())        {            System.out.println(m.group() "\n子串起始位置:" m.start() "\n子串结束位置:" m.end());        }    }}
  • 运行程序结果如下:
lancibe@lancibe-TM1701:~/java/test/src/chapter7$ java StartEnd 目标字符串:Java is not very easy :(Java子串起始位置:0子串结束位置:4is子串起始位置:5子串结束位置:7not子串起始位置:8子串结束位置:11very子串起始位置:12子串结束位置:16easy子串起始位置:17子串结束位置:21
  • matches()和lookingAt()方法有点类似,只是matches()方法要求整个字符串和Pattern完全匹配才返回true,而lookingAt()只要求字符串以Pattern开头就会返回true。Reset()方法可以将现有的Matcher对象应用于新的字符序列:
import java.util.regex.Matcher;import java.util.regex.Pattern;public class MatchesTest {    public static void main(String[] args)    {        String[] mails =                {                        "lancibe@yahoo.com",                        "lancibe0326@gmail.com",                        "1609547089@qq.com",                        "lancibe@xiyoulinux.com",                        "123@abc.xx"                };        String mailRegEx = "\\w{3,20}@\\w \\.(com|org|cn|net|gov)";        Pattern mailPattern = Pattern.compile(mailRegEx);        Matcher matcher = null;        for (String mail:mails)        {            if(matcher == null)            {                matcher = mailPattern.matcher(mail);            }            else            {                matcher.reset(mail);            }            String result = mail (matcher.matches() ? "是" : "不是" )  "一个有效的邮件地址";            System.out.println(result);        }    }}
  • 除此之外,还可以利用正则表达式对目标字符串进行分割、查找、替换等操作:
import java.util.regex.Matcher;import java.util.regex.Pattern;public class ReplaceTest {    public static void main(String[] args)    {        String[] msgs=                {                        "Java has regular expressions in 1.4",                        "regular expressions now expressing is Java",                        "Java represses oracular expressions"                };        Pattern p = Pattern.compile("re\\w ");        Matcher match = null;        for (int i = 0; i < msgs.length ; i  )        {            if(match == null)            {                match = p.matcher(msgs[i]);            }            else            {                match.reset(msgs[i]);            }            System.out.println(match.replaceAll("哈哈:)"));        }    }}
  • 实际上,String类中也提供了replaceAll()、replaceFirst()、split()等方法。
import java.util.Arrays;public class StringReg {    public static void main(String[] args)    {        String[] msgs =                {                        "Java has regular expressions in 1.4",                        "regular expressions now expressing is Java",                        "Java represses oracular expressions"                };        for(String msg:msgs)        {            System.out.println(msg.replaceFirst("re\\w*", "哈哈:)"));            System.out.println(Arrays.toString(msg.split(" ")));        }    }}
  • 程序运行结果如下:
lancibe@lancibe-TM1701:~/java/test/src/chapter7$ java StringReg Java has 哈哈:) expressions in 1.4[Java, has, regular, expressions, in, 1.4]哈哈:) expressions now expressing is Java[regular, expressions, now, expressing, is, Java]Java 哈哈:) oracular expressions[Java, represses, oracular, expressions]

变量处理和方法处理

  • Java9 引入了一个新的VarHandle类,并增强了原有的MethodHandle类。通过这两个类,允许Java像动态语言一样引用变量、引用方法,并调用他们。

Java9 增强的MethodHandle

  • 该类为Java增加了方法引用的功能,方法引用的概念有点类似于C语言的函数指针。这种方法引用是一种轻量级的引用方式,他不会检查方法的访问权限,也不管方法所属的类、实例方法或静态方法,MethodHandle就是简单代表特定的方法,并可通过该类来调用方法。
  • 为了使用该类,还涉及如下几个类:
    • MethodHandles:MethodHandle的工厂类,他提供了一系列静态方法用于获取MethodHandle
    • MethodHandles.Lookup静态内部类,他也是MethodHandle、VarHandle的工厂类,专门用于获取MethodHandle和VarHandle
    • MethodType代表一个方法类型。MethodType根据方法的形参、返回值类型来确定方法类型
import java.lang.invoke.MethodHandle;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodType;public class MethodHandleTest {    //定义一个private类方法    private static void hello()    {        System.out.println("Hello world!");    }    //定义一个private实例方法    private String hello(String name)    {        System.out.println("执行带参数的hello"   name);        return name ",你好";    }    public static void main(String[] args)            throws Throwable    {        //定义一个返回值为void、不带形参的方法类型        MethodType type = MethodType.methodType(void.class);        //使用MethodHandles.Lookup的findStatic获取类方法        MethodHandle mtd = MethodHandles.lookup().findStatic(MethodHandleTest.class, "hello", type);        //通过MethodHandle执行方法        mtd.invoke();        //使用MethodHandles.Lookup的findVirtual获取实例方法        MethodHandle mtd2 = MethodHandles.lookup().findVirtual(MethodHandleTest.class, "hello",                //指定获取返回值为String、形参为String的方法类型                MethodType.methodType(String.class, String.class));        //通过MethodHandle执行方法,传入主调对象和参数        System.out.println(mtd2.invoke(new MethodHandleTest(), "Lancibe"));    }}
  • 从上面可以看出,程序使用MethodHandles.lookup对象根据类、方法名、方法类型来获取MethodHandle对象。由于此处的方法名只是一个字符串,而该字符串可以来自于变量、配置文件等,这意味着通过MethodHandle可以让Java动态调用某一个方法。

Java9 增加的VarHandle

  • VarHandle主要用于动态操作数租的元素或对象的成员变量。VarHandle与MethodHandle非常相似,他也需要通过MethodHandles来获取实例,接下来调用VarHandle的方法即可动态操作数租的指定数组的元素或指定对象的成员变量。
import java.lang.invoke.MethodHandles;import java.lang.invoke.VarHandle;import java.util.Arrays;class User{    String name;    static int MAX_AGE;}public class VarHandleTest {    public static void main(String[] args)            throws Throwable    {        String[] sa = new String[]{"Lancibe", "love", "java"};        //获取一个String[]数组的VarHandle对象        VarHandle avh = MethodHandles.arrayElementVarHandle(String[].class);        //比较并设置:如果第三个元素是java,则该元素被设置成python        boolean r = avh.compareAndSet(sa, 2, "java", "python");        //输出比较结果        System.out.println(r);        //看到第三个元素被替换        System.out.println(Arrays.toString(sa));        //获取sa数组的第二个元素        System.out.println(avh.get(sa,1));        //获取并设置:返回第三个元素,并将第三个元素设置为C          System.out.println(avh.getAndSet(sa, 2, "C  "));        //看到第三个元素被替换成C          System.out.println(Arrays.toString(sa));        //用findVarHandle方法获取User类中名为name、类型为String的实例变量        VarHandle vh1 = MethodHandles.lookup().findVarHandle(User.class, "name", String.class);        User user = new User();        System.out.println(vh1.get(user));        //通过VarHandle设置指定实例变量的值        vh1.set(user, "孙悟空");        //输出user的name实例变量的值        System.out.println(user.name);        //用findVarHandle方法获取User类中名为MAX_AGE、类型为Integer的类变量        VarHandle vh2 = MethodHandles.lookup().findStaticVarHandle(User.class,"MAX_AGE", int.class);        //通过VarHandle获取指定类变量的值        System.out.println(vh2.get());        //通过VarHandle设置指定类变量的值        vh2.set(100);        //输出User的MAX_AGE类变量        System.out.println(User.MAX_AGE);    }}

来源:https://www.icode9.com/content-1-769651.html

(0)

相关推荐

  • 2w字 详解 String,yyds

    回复"000"获取程序员必备电子书 前言 大家好,我是老田,今天给大家分享java基础知识之String. String类的重要性就不必说了,可以说是我们后端开发用的最多的类,所以 ...

  • 前端教程:JavascriptString对象

    String对象与一系列字符和包装JavaScript的字符串原始数据类型一些辅助方法. 因为JavaScript字符串元和String对象之间自动转换,可以调用任何String对象的字符串原始的辅助 ...

  • 《数据可视化基础》第七章:分布可视化:经验累积分布函数和q-q图

    以下部分是基于<Fundamentals of Data Visualization>学习笔记,要是有兴趣的话,可以直接看原版书籍:https://serialmentor.com/dat ...

  • 第七章:克服频遗关

    如果把戒色比作一项运动,可以说是铁人三项.断意淫是其中一个专项,警惕意识也是一个专项,如何控制遗精也是一个专项,情绪管理也是一项,养生意识也是一项,学习意识也是一项.准确地说,应该是"戒色六 ...

  • Java基础之:泛型

    Java基础之:泛型 在不使用泛型的情况下,在ArrayList 中,添加3个Dog. Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx()). package ...

  • 『中医偏方精品』第七章 神经精神系统疾病

    第七章  神经精神系统疾病 一.面神经麻痹 面神经麻痹又称面神经炎,是一种急性非脓性的茎乳孔内的面神经炎.病因一般认为与风湿病及面神经管内的骨膜水肿有关.中医认为本病为"口眼歪斜" ...

  • 宝利老师导读《苏东坡传》第七章

    第七章 本章开始出现本书最重要男二号,王安石.可以说,这一章就是王安石的微型传记上.要点两个. 一.王安石其人. 原文最有概括性的一段:王安石是个怪人,思想人品都异乎寻常.学生时代很勤勉,除去语言学极 ...

  • Java 基础知识

    Java 基础知识

  • (尾声篇)人市 第七章 || 田国彬(河北藁城)

    第 七 章     到了家里,天已经很晚了,来我家串门的邻居好几个,都是一门心思,你挣了多少钱,我躺在床上,支撑着腰坐起来,自豪地说:"二十块."     货货问我:"你 ...

  • 良书上的第七章里的几条偏方 必须收藏

    感冒鼻塞 葱白10根以上,生姜片50克,陈皮4片,淡豆豉20克,冰糖适量.入陶罐用水煮,煮熟去渣,乘热喝,然后盖棉被发汗,喝两三次有效. 高血压 白颈活蚯蚓15条,白糖100克.将蚯蚓剖开,洗净泥土, ...

  • Java 基础语法

    注释 #单行注释 // 这里是单行注释 #多行注释 /* 这里是 多行注释 */ #JavaDoc /* *@Description: *@Author: */ Java可以使用中文命名 但不建议使用 ...