Java基础重点总结

5 篇文章 0 订阅
4 篇文章 0 订阅

数据类型

包装类型

Java 八个基本类型,基本类型都有对应的包装类型:

基本类型大小(bit)最小值最大值默认值包装类
boolean1--falseBoolean
char16Unicode 0Unicode 2^16-1\u0000(Unicode 0)Character
byte8-128127(byte)0Byte
short16-2^152^15-1(short)0Short
int32-2^312^31-10Int
long64-2^632^63-10LLong
float32--0.0FFloat
double64--0.0DDouble

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

//自动装箱和自动拆箱
//1、自动装
Integer x=4; //自动装箱 (基本类型转化为包装类型)
//实际上就是 Integer x=Integer.valueOf(4);
//2、自动拆箱
x=x+5;
x=Integer.valueOf(x.intValue()+5);//x.intValue()就是拆箱-->先拆箱,再装箱。

缓存池

Java 基本类型的包装类的大部分都实现了缓存池技术,对应的缓冲池如下:

  • Boolean 直接返回 true / false
  • Byte/ Short / Integer / Long 创建了数值范围在 [-128,127] 的相应类型的缓存数据
  • Character 创建了数值在 [0,127] 的缓存数据

如果超出对应范围仍然会去创建新的对象。

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

  • new Integer(123) 每次都会新建一个对象;
  • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);    // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k);   // true

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

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

在 Java 8 中,Integer 缓存池的大小默认为 -128~127

static final int low = -128;
static final int high;
static final Integer cache[];

static {
    // high value may be configured by property
    int h = 127;
    String integerCacheHighPropValue =
        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    if (integerCacheHighPropValue != null) {
        try {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        } catch( NumberFormatException nfe) {
            // If the property cannot be parsed into an int, ignore it.
        }
    }
    high = h;

    cache = new Integer[(high - low) + 1];
    int j = low;
    for(int k = 0; k < cache.length; k++)
        cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
}

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

Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true

+ 不适用于 Integer 对象,首先 i,j 进行自动拆箱,然后数值相加,得到数值 80,进行自动装箱后,和 m 引用相同的对象。Integer 对象无法与数值直接进行比较,自动拆箱后转为数值 80,显然 80 == 80,输出 true。

Integer i = 40;
Integer j = 40;
Integer m = 80;
System.out.println(m == i+j);  // true
System.out.println(80 == i+j);  // true

**

Object 常用方法

**

Object 通用方法

Object 类中的方法一览:


public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

*equals()

1. 等价关系

Ⅰ 自反性

x.equals(x); // true

Ⅱ 对称性

x.equals(y) == y.equals(x); // true

Ⅲ 传递性

if (x.equals(y) && y.equals(z))
    x.equals(z); // true;

Ⅳ 一致性

多次调用 equals() 方法结果不变

x.equals(y) == x.equals(y); // true

Ⅴ 与 null 的比较

对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false

x.equals(null); // false;

2. 等价与相等

  • 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
  • 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

3. 实现

  • 检查是否为同一个对象的引用,如果是直接返回 true;
  • 检查是否是同一个类型,如果不是,直接返回 false;
  • 将 Object 对象进行转型;
  • 判断每个关键域是否相等。
public class EqualExample {

    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;  //检查是否为同一个对象的引用,如果是直接返回 true;
        if (o == null || getClass() != o.getClass()){
            //检查是否是同一个类型,如果不是,直接返回 false
            return false;
        }

        // 将 Object 对象进行转型
        EqualExample that = (EqualExample) o;

        // 判断每个关键域是否相等。
        if (x != that.x) return false;
        if (y != that.y) return false;
        return z == that.z;
    }
}

*hashCode()

hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。
等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价

在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等

下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。
我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,
因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。
这就要求了散列函数要把所有域的值都考虑进来
可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。
R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。

一个数与 31 相乘可以转换成移位和减法:31*x == (x<<5)-x,编译器会自动进行这个优化。

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}

了解:IDEA中 Alt+Insert 快捷键就可以快速生成 hashCode() 和 equals() 方法。

toString()

默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。

public class ToStringExample {

    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}
ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());
ToStringExample@4554617c

clone()

1. Cloneable

clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。

public class CloneExample {
    private int a;
    private int b;
}
CloneExample e1 = new CloneExample();
// CloneExample e2 = e1.clone(); 
// 'clone()' has protected access in 'java.lang.Object'

重写 clone() 得到以下实现:

public class CloneExample {
    private int a;
    private int b;

    // CloneExample 默认继承 Object
    @Override
    public CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}
CloneExample e1 = new CloneExample();
try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
java.lang.CloneNotSupportedException: CloneExample

以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。

应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法

Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException

public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2. 浅拷贝

拷贝对象和原始对象的引用类型引用同一个对象。

public class ShallowCloneExample implements Cloneable {

    private int[] arr;

    public ShallowCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ShallowCloneExample clone() throws CloneNotSupportedException {
        return (ShallowCloneExample) super.clone();
    }
}
// 拷贝对象和原始对象的引用类型引用同一个对象。
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 222

3. 深拷贝

拷贝对象和原始对象的引用类型引用不同对象。

public class DeepCloneExample implements Cloneable {

    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        // 创建新对象
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 2

4. clone() 的替代方案

使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。
Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象

public class CloneConstructorExample {

    private int[] arr;

    public CloneConstructorExample() { //构造函数
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) { // 拷贝构造函数
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 2

关键字

final 和 static 关键字

final 关键字

1. 数据

声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。

  • 对于基本类型,final 使数值不变;
  • 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
final int x = 1;
// x = 2;  // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;

2. 方法

声明方法不能被子类重写

private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。

3. 类

声明类不允许被继承

static 关键字

1. 静态变量

  • 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份
  • 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
public class A {

    private int x;         // 实例变量
    private static int y;  // 静态变量

    public static void main(String[] args) {
        // int x = A.x;  
        // Non-static field 'x' cannot be referenced from a static context
        
        A a = new A();
        int x = a.x;
        int y = A.y;
    }
}

2. 静态方法

静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。

public abstract class A {
    public static void func1(){
    }
    // public abstract static void func2();  
    // Illegal combination of modifiers: 'abstract' and 'static'
    // 静态方法必须有实现,也就是说它不能是抽象方法。
}

只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。

public class A {

    private static int x;
    private int y;

    public static void func1(){
        int a = x;
        // int b = y;  
        // Non-static field 'y' cannot be referenced from a static context
        // int b = this.y;     // 'A.this' cannot be referenced from a static context
    }
}

3. 静态语句块

静态语句块在类初始化时运行一次。

public class A {
    static {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
    }
}
123

4. 静态内部类

非静态内部类依赖于外部类的实例,而静态内部类不需要。

public class OuterClass {

    class InnerClass { // 非静态内部类
    }

    static class StaticInnerClass { // 静态内部类
    }

    public static void main(String[] args) {
        // InnerClass innerClass = new InnerClass(); 
        // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

静态内部类不能访问外部类的非静态的变量和方法。

5. 静态导包

在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。

import static com.xxx.ClassName.*

6. 初始化顺序

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。

// 静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序
public static String staticField = "静态变量";

static {
    System.out.println("静态语句块");
}
public String field = "实例变量";
{
    System.out.println("普通语句块");
}

最后才是构造函数的初始化。

public InitialOrderTest() {
    System.out.println("构造函数");
}

存在继承的情况下,初始化顺序为:

  • 父类(静态变量、静态语句块)
  • 子类(静态变量、静态语句块)
  • 父类(实例变量、普通语句块)
  • 父类(构造函数)
  • 子类(实例变量、普通语句块)
  • 子类(构造函数)

**

反射

**

异常-Exception

异常的概念

Java 异常是一个描述在代码段中发生异常的对象,当发生异常情况时,一个代表该异常的对象被创建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常。

异常继承体系

Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: ErrorException

  • Error:通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些错误时,建议终止程序;
  • Exception:通常情况下是可以被程序处理的,捕获后可能恢复,并且在程序中应该尽可能地去处理这些异常。

Java 异常分为两种:

  • 受检异常:除了 RuntimeException 及其子类以外,其他的 Exception 类及其子类都属于这种异常
  • 非受检异常:包括 RuntimeException 及其子类和 Error。

注意:非受检查异常为编译器不要求强制处理的异常,受检异常则是编译器要求必须处置的异常。

Exception 这类异常分为运行时异常非运行时异常(编译异常)

  • 运行时异常 :包括 RuntimeException 及其子类。比如 NullPointerException、IndexOutOfBoundsException。属于非受检异常,可以进行捕捉处理,也可以不处理。
  • 非运行时异常(编译异常):RuntimeExcaption 以外的 Exception。IOException、SQLException 已经自定义的异常,必须要进行处理。

Java 异常的处理机制

Java 异常处理机制本质上就是抛出异常捕捉异常

抛出异常

i.普通问题:指在当前环境下能得到足够的信息,总能处理这个错误。

ii.异常情形:是指阻止当前方法或作用域继续执行的问题。对于异常情形,已经程序无法执行继续下去了,
因为在当前环境下无法获得必要的信息来解决问题,我们所能做的就是从当前环境中跳出,并把问题提交给上一级环境,
这就是抛出异常时所发生的事情。

iii.抛出异常后,会有几件事随之发生:

第一:像创建普通的java对象一样将使用new在堆上创建一个异常对象

第二:当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。

此时,异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序
这个恰当的地方就是异常处理程序或者异常处理器,
它的任务是将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去。

捕捉异常

在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。
潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。
当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。
运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。
当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

注意:

对于运行时异常、错误和受检异常,Java技术所要求的异常处理方式有所不同。

(1)由于运行时异常及其子类的不可查性,为了更合理、更容易地实现应用程序,
Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常

(2)对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。
因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。

(3)对于所有的受检异常,
Java规定:一个方法必须捕捉,或者声明抛出方法之外。
也就是说,当一个方法选择不捕捉受检异常时,它必须声明将抛出异常。

Java 异常的处理原则

  • 具体明确:抛出的异常应能通过异常类名和message准确说明异常的类型和产生异常的原因;
  • 提早抛出:应尽可能早地发现并抛出异常,便于精确定位问题;
  • 延迟捕获:异常的捕获和处理应尽可能延迟,让掌握更多信息的作用域来处理异常

Java 常见异常以及错误

类型说明
RuntimeException 子类
java.lang.ArrayIndexOutOfBoundsException数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出
java.lang.ArithmeticException算术条件异常。譬如:整数除零等
java.lang.NullPointerException空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
java.lang.ClassNotFoundException找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常
java.lang.SecurityException安全性异常
java.lang.IllegalArgumentException非法参数异常
IOException
IOException操作输入流和输出流时可能出现的异常
EOFException文件已结束异常
FileNotFoundException文件未找到异常
其他
ClassCastException类型转换异常类
ArrayStoreException数组中包含不兼容的值抛出的异常
SQLException操作数据库异常类
NoSuchFieldException字段未找到异常
NumberFormatException字符串转换为数字抛出的异常
StringIndexOutOfBoundsException字符串索引超出范围抛出的异常
IllegalAccessException不允许访问某类异常
Error
NoClassDefFoundError找不到class定义的错误
StackOverflowError深递归导致栈被耗尽而抛出的错误
OutOfMemoryError内存溢出错误

try-catch-finally语句块的执行


(1) try 块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。

(2) catch 块:用于处理try捕获到的异常。

(3) finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。
当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

在以下 4 种特殊情况下,finally 语句块不会被执行:

  • 在finally语句块中发生了异常
  • 在前面的代码中用了System.exit()退出程序。
  • 程序所在的线程死亡。
  • 关闭 CPU。

try catch代码块的性能如何

  • 会影响 JVM 的重排序优化;
  • 异常对象实例需要保存栈快照等信息,开销比较大。

try-with-resources

适用范围:任何实现 java.lang.AutoCloseable 或者 java.io.Closeable 的对象。

在 try-with-resources 语句中,任何 catch 或 finally 代码块在声明的资源关闭后运行。

使用 try-catch-finally 关闭资源:

//读取文本文件的内容
Scanner scanner = null;
try {
    scanner = new Scanner(new File("D://read.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}

使用 try-with-resources 语句改造上面的代码:

try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

使用 try-with-resources 语句关闭多个资源:

try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));  // 这里采用 ";" 进行分割
     BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
    int b;
    while ((b = bin.read()) != -1) {
        bout.write(b);
    }
}
catch (IOException e) {
    e.printStackTrace();
}

final & finally & finalize

final:最终的意思,可以修饰类,修饰成员变量,修饰成员方法

修饰类:类不能被继承

修饰变量:变量是常量

修饰方法:方法不能被重写(Override)

(2)finally:是异常处理的关键字,用于释放资源。一般来说,代码必须执行(特殊情况:在执行到finally JVM就退出了)

(3)finalize:是Object的一个方法,用于垃圾回收。

看程序写结果

public class TestException {
    public static void main(String[] args) {
        System.out.println(getInt());
    }

    public static int getInt(){
        int a=10;
        try{
            System.out.println(a/0);
        }catch (ArithmeticException e){
            a=30;
            return a; 
        }finally {
            a=40;
        }
        System.out.println("a="+a);
        return a;
    }
}

结果为:

30

反射-Reflection

反射概念

Java 的反射机制是指在运行状态中:

  • 对任意一个类,都能够知道这个类的所有属性和方法
  • 对任意一个对象,都能调用这个对象的所有属性和方法

反射的作用

  • 检查类的属性和方法
  • 在运行时检查对象的类型
  • 动态构造某个类的对象
  • 可以任意调用对象的方法

反射的使用

1. java.lang.reflect 包中的三个类

  • Field:成员变量
  • Method:成员方法
  • Constructor:构造方法

2. 对 public 域的方法

包括父类成员。

  • getField
  • getMethod
  • getConstructor

3. 对其他域的方法

包括 private 和 protected 的成员,但不包括父类成员。

  • getDeclaredField
  • getDeclaredMethod
  • getDeclaredConstructor

利用反射访问私有属性:使用 setAccessible(true)

反射的不足

性能是一个问题。反射相当于一系列解释操作,通知 Java 虚拟机要做什么,性能比直接的 Java 要慢很多。

泛型

(繁星)

异常-Exception

异常的概念

Java 异常是一个描述在代码段中发生异常的对象,当发生异常情况时,一个代表该异常的对象被创建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常。

异常继承体系

Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: ErrorException

  • Error:通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些错误时,建议终止程序;
  • Exception:通常情况下是可以被程序处理的,捕获后可能恢复,并且在程序中应该尽可能地去处理这些异常。

Java 异常分为两种:

  • 受检异常:除了 RuntimeException 及其子类以外,其他的 Exception 类及其子类都属于这种异常
  • 非受检异常:包括 RuntimeException 及其子类和 Error。

注意:非受检查异常为编译器不要求强制处理的异常,受检异常则是编译器要求必须处置的异常。

Exception 这类异常分为运行时异常非运行时异常(编译异常)

  • 运行时异常 :包括 RuntimeException 及其子类。比如 NullPointerException、IndexOutOfBoundsException。属于非受检异常,可以进行捕捉处理,也可以不处理。
  • 非运行时异常(编译异常):RuntimeExcaption 以外的 Exception。IOException、SQLException 已经自定义的异常,必须要进行处理。

Java 异常的处理机制

Java 异常处理机制本质上就是抛出异常捕捉异常

抛出异常

i.普通问题:指在当前环境下能得到足够的信息,总能处理这个错误。

ii.异常情形:是指阻止当前方法或作用域继续执行的问题。对于异常情形,已经程序无法执行继续下去了,
因为在当前环境下无法获得必要的信息来解决问题,我们所能做的就是从当前环境中跳出,并把问题提交给上一级环境,
这就是抛出异常时所发生的事情。

iii.抛出异常后,会有几件事随之发生:

第一:像创建普通的java对象一样将使用new在堆上创建一个异常对象

第二:当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。

此时,异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序
这个恰当的地方就是异常处理程序或者异常处理器,
它的任务是将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去。

捕捉异常

在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。
潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。
当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。
运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。
当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

注意:

对于运行时异常、错误和受检异常,Java技术所要求的异常处理方式有所不同。

(1)由于运行时异常及其子类的不可查性,为了更合理、更容易地实现应用程序,
Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常

(2)对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。
因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。

(3)对于所有的受检异常,
Java规定:一个方法必须捕捉,或者声明抛出方法之外。
也就是说,当一个方法选择不捕捉受检异常时,它必须声明将抛出异常。

Java 异常的处理原则

  • 具体明确:抛出的异常应能通过异常类名和message准确说明异常的类型和产生异常的原因;
  • 提早抛出:应尽可能早地发现并抛出异常,便于精确定位问题;
  • 延迟捕获:异常的捕获和处理应尽可能延迟,让掌握更多信息的作用域来处理异常

Java 常见异常以及错误

类型说明
RuntimeException 子类
java.lang.ArrayIndexOutOfBoundsException数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出
java.lang.ArithmeticException算术条件异常。譬如:整数除零等
java.lang.NullPointerException空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
java.lang.ClassNotFoundException找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常
java.lang.SecurityException安全性异常
java.lang.IllegalArgumentException非法参数异常
IOException
IOException操作输入流和输出流时可能出现的异常
EOFException文件已结束异常
FileNotFoundException文件未找到异常
其他
ClassCastException类型转换异常类
ArrayStoreException数组中包含不兼容的值抛出的异常
SQLException操作数据库异常类
NoSuchFieldException字段未找到异常
NumberFormatException字符串转换为数字抛出的异常
StringIndexOutOfBoundsException字符串索引超出范围抛出的异常
IllegalAccessException不允许访问某类异常
Error
NoClassDefFoundError找不到class定义的错误
StackOverflowError深递归导致栈被耗尽而抛出的错误
OutOfMemoryError内存溢出错误

try-catch-finally语句块的执行


(1) try 块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。

(2) catch 块:用于处理try捕获到的异常。

(3) finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。
当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

在以下 4 种特殊情况下,finally 语句块不会被执行:

  • 在finally语句块中发生了异常
  • 在前面的代码中用了System.exit()退出程序。
  • 程序所在的线程死亡。
  • 关闭 CPU。

try catch代码块的性能如何

  • 会影响 JVM 的重排序优化;
  • 异常对象实例需要保存栈快照等信息,开销比较大。

try-with-resources

适用范围:任何实现 java.lang.AutoCloseable 或者 java.io.Closeable 的对象。

在 try-with-resources 语句中,任何 catch 或 finally 代码块在声明的资源关闭后运行。

使用 try-catch-finally 关闭资源:

//读取文本文件的内容
Scanner scanner = null;
try {
    scanner = new Scanner(new File("D://read.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}

使用 try-with-resources 语句改造上面的代码:

try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

使用 try-with-resources 语句关闭多个资源:

try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));  // 这里采用 ";" 进行分割
     BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
    int b;
    while ((b = bin.read()) != -1) {
        bout.write(b);
    }
}
catch (IOException e) {
    e.printStackTrace();
}

final & finally & finalize

final:最终的意思,可以修饰类,修饰成员变量,修饰成员方法

修饰类:类不能被继承

修饰变量:变量是常量

修饰方法:方法不能被重写(Override)

(2)finally:是异常处理的关键字,用于释放资源。一般来说,代码必须执行(特殊情况:在执行到finally JVM就退出了)

(3)finalize:是Object的一个方法,用于垃圾回收。

看程序写结果

public class TestException {
    public static void main(String[] args) {
        System.out.println(getInt());
    }

    public static int getInt(){
        int a=10;
        try{
            System.out.println(a/0);
        }catch (ArithmeticException e){
            a=30;
            return a; 
        }finally {
            a=40;
        }
        System.out.println("a="+a);
        return a;
    }
}

结果为:

30

反射-Reflection

反射概念

Java 的反射机制是指在运行状态中:

  • 对任意一个类,都能够知道这个类的所有属性和方法
  • 对任意一个对象,都能调用这个对象的所有属性和方法

反射的作用

  • 检查类的属性和方法
  • 在运行时检查对象的类型
  • 动态构造某个类的对象
  • 可以任意调用对象的方法

反射的使用

1. java.lang.reflect 包中的三个类

  • Field:成员变量
  • Method:成员方法
  • Constructor:构造方法

2. 对 public 域的方法

包括父类成员。

  • getField
  • getMethod
  • getConstructor

3. 对其他域的方法

包括 private 和 protected 的成员,但不包括父类成员。

  • getDeclaredField
  • getDeclaredMethod
  • getDeclaredConstructor

利用反射访问私有属性:使用 setAccessible(true)

反射的不足

性能是一个问题。反射相当于一系列解释操作,通知 Java 虚拟机要做什么,性能比直接的 Java 要慢很多。

Java 常见类 I

Object

Object类是类层次结构的根类。每个类都使用 Object 作为超类。每个类都直接或者间接的继承自Object类。

Object 中常用方法有:

public int hashCode() //返回该对象的哈希码值。
// 注意:哈希值是根据哈希算法计算出来的一个值,这个值和地址值有关,但是不是实际地址值。

public final Class getClass() //返回此 Object 的运行时类

public String toString() //返回该对象的字符串表示。

protected Object clone() //创建并返回此对象的一个副本。可重写该方法

protected void finalize() 
// 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。用于垃圾回收,但是什么时候回收不确定。

equals() 方法

1. 等价关系

Ⅰ 自反性

x.equals(x); // true

Ⅱ 对称性

x.equals(y) == y.equals(x); // true

Ⅲ 传递性

if (x.equals(y) && y.equals(z))
    x.equals(z); // true;

Ⅳ 一致性

多次调用 equals() 方法结果不变

x.equals(y) == x.equals(y); // true

Ⅴ 与 null 的比较

对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false

x.equals(null); // false;

2. 等价与相等

  • 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
  • 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

3. 实现

  • 检查是否为同一个对象的引用,如果是直接返回 true;
  • 检查是否是同一个类型,如果不是,直接返回 false;
  • 将 Object 对象进行转型;
  • 判断每个关键域是否相等。
public class EqualExample {

    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;  //检查是否为同一个对象的引用,如果是直接返回 true;
        if (o == null || getClass() != o.getClass()){
            //检查是否是同一个类型,如果不是,直接返回 false
            return false;
        }

        // 将 Object 对象进行转型
        EqualExample that = (EqualExample) o;

        // 判断每个关键域是否相等。
        if (x != that.x) return false;
        if (y != that.y) return false;
        return z == that.z;
    }
}

hashCode() 方法

hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。
等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价

在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等

下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。
我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,
因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。
这就要求了散列函数要把所有域的值都考虑进来
可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。
R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。

一个数与 31 相乘可以转换成移位和减法:31*x == (x<<5)-x,编译器会自动进行这个优化。

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}

了解:IDEA中 Alt+Insert 快捷键就可以快速生成 hashCode() 和 equals() 方法。

toString() 方法

默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。

public class ToStringExample {

    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}
ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());
ToStringExample@4554617c

clone() 方法

1. Cloneable

clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。

public class CloneExample {
    private int a;
    private int b;
}
CloneExample e1 = new CloneExample();
// CloneExample e2 = e1.clone(); 
// 'clone()' has protected access in 'java.lang.Object'

重写 clone() 得到以下实现:

public class CloneExample {
    private int a;
    private int b;

    // CloneExample 默认继承 Object
    @Override
    public CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}
CloneExample e1 = new CloneExample();
try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
java.lang.CloneNotSupportedException: CloneExample

以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。

应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法

Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException

public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2. 浅拷贝

拷贝对象和原始对象的引用类型引用同一个对象。

public class ShallowCloneExample implements Cloneable {

    private int[] arr;

    public ShallowCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ShallowCloneExample clone() throws CloneNotSupportedException {
        return (ShallowCloneExample) super.clone();
    }
}
// 拷贝对象和原始对象的引用类型引用同一个对象。
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 222

3. 深拷贝

拷贝对象和原始对象的引用类型引用不同对象。

public class DeepCloneExample implements Cloneable {

    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        // 创建新对象
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 2

4. clone() 的替代方案

使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。
Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象

public class CloneConstructorExample {

    private int[] arr;

    public CloneConstructorExample() { //构造函数
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) { // 拷贝构造函数
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e1.get(2)); // 222
System.out.println(e2.get(2)); // 2

String

String 被声明为 final,因此它不可被继承。

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

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

构造方法

public String() //空构造

public String(byte[] bytes) //把字节数组转成字符串

public String(byte[] bytes,int index,int length) //把字节数组的一部分转成字符串

public String(char[] value) //把字符数组转成字符串

public String(char[] value,int index,int count) //把字符数组的一部分转成字符串,第三个参数表示的是数目

public String(String original) //把字符串常量值转成字符串

使用示例:

public class StringDemo {
    public static void main(String[] args) {
        //public String():空构造
        String s=new String();
        System.out.println("s:"+s);
        System.out.println("s.length="+s.length());
        System.out.println("-----------------------");

        //public String(byte[] bytes):把字节数组转成字符串
        byte[] bys={97,98,99,100,101};

        String s2=new String(bys);
        System.out.println("s2:"+s2);
        System.out.println("s2.length="+s2.length());
        System.out.println("-----------------------");

        //public String(byte[] bytes,int index,int length):把字节数组的一部分转成字符串
        String s3=new String(bys,0,3); //从0位置开始,3个字符
        System.out.println("s3:"+s3);//s3:abc
        System.out.println("s3.length="+s3.length());//s3.length=3
        System.out.println("-----------------------");


        //public String(char[] value):把字符数组转成字符串
        char[] chs={'a','b','c','d','e'};
        String s4=new String(chs); //从0位置开始,3个字符
        System.out.println("s4:"+s4);
        System.out.println("s4.length="+s4.length());
        System.out.println("-----------------------");

        //public String(char[] value,int index,int count):把字符数组的一部分转成字符串
        String s5=new String(chs,0,3); //从0位置开始,3个字符
        System.out.println("s5:"+s5);
        System.out.println("s5.length="+s5.length());
        System.out.println("-----------------------");

        //public String(String original):把字符串常量值转成字符串
        String s6=new String("abcde");
        System.out.println("s6:"+s6);
        System.out.println("s6.length="+s6.length());
    }
}

字符串的特点:一旦被赋值,就不能改变

public static void test() {
    String s = "hello";
    s += "world";
    System.out.println("s:" + s); // helloworld
}

小练习:

public static void test2(){
    String s1 = new String("hello");
    String s2 = new String("hello");
    System.out.println(s1 == s2); // false
    System.out.println(s1.equals(s2)); // true

    String s3 = new String("hello");
    String s4 = "hello";
    System.out.println(s3 == s4); // false
    System.out.println(s3.equals(s4)); // true

    String s5 = "hello";
    String s6 = "hello";
    System.out.println(s5 == s6); // true
    System.out.println(s5.equals(s6)); // true
}

判断功能

boolean equals(Object obj) //比较字符串的内容是否相同,区分大小写

boolean equalsIgnoreCase(String str) //比较字符串的内容是否相同,忽略大小写

boolean contains(String str) //判断大字符串中是否包含小字符串

boolean startsWith(String str) //判断字符串是否以某个指定的字符串开头

boolean endsWith(String str) //判断字符串是否以某个指定的字符串结尾

boolean isEmpty()// 判断字符串是否为空

注意:字符串内容为空和字符串对象为空。

String s = "";//字符串内容为空
String s = null;//字符串对象为空

使用示例:

public class StringDemo3 {
    public static void main(String[] args) {
        // 创建字符串对象
        String s1 = "helloworld";
        String s2 = "helloworld";
        String s3 = "HelloWorld";

        // boolean equals(Object obj):比较字符串的内容是否相同,区分大小写
        System.out.println("equals:" + s1.equals(s2));//true
        System.out.println("equals:" + s1.equals(s3));//false
        System.out.println("-----------------------");

        // boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
        System.out.println("equals:" + s1.equalsIgnoreCase(s2));//true
        System.out.println("equals:" + s1.equalsIgnoreCase(s3));//true
        System.out.println("-----------------------");

        // boolean contains(String str):判断大字符串中是否包含小字符串
        System.out.println("contains:" + s1.contains("hello"));//true
        System.out.println("contains:" + s1.contains("hw"));//false
        System.out.println("-----------------------");

        // boolean startsWith(String str):判断字符串是否以某个指定的字符串开头
        System.out.println("startsWith:" + s1.startsWith("h"));//true
        System.out.println("startsWith:" + s1.startsWith("hello"));//true
        System.out.println("startsWith:" + s1.startsWith("world"));//false
        System.out.println("-----------------------");

        //boolean endsWith(String str):判断字符串是否以某个指定的字符串结尾
        System.out.println("startsWith:" + s1.endsWith("d"));//true
        System.out.println("startsWith:" + s1.endsWith("world"));//true
        System.out.println("startsWith:" + s1.endsWith("hello"));//false
        System.out.println("-----------------------");

        // boolean isEmpty():判断字符串是否为空。
        System.out.println("isEmpty:" + s1.isEmpty());//false

        String s4 = ""; //字符串内容为空
        String s5 = null;//字符串对象为空
        System.out.println("isEmpty:" + s4.isEmpty());//true
        // NullPointerException
        // s5对象都不存在,所以不能调用方法,空指针异常
        //System.out.println("isEmpty:" + s5.isEmpty());
    }
}

获取功能

int length() //获取字符串的长度。

char charAt(int index) //获取指定索引位置的字符

int indexOf(int ch) //返回指定字符在此字符串中第一次出现处的索引。为什么这里参数int类型,而不是char类型?原因是:'a'和97其实都可以代表'a'

int indexOf(String str) //返回指定字符串在此字符串中第一次出现处的索引。

int indexOf(int ch,int fromIndex) //返回指定字符在此字符串中从指定位置后第一次出现处的索引。

int indexOf(String str,int fromIndex) //返回指定字符串在此字符串中从指定位置后第一次出现处的索引。

String substring(int start) //从指定位置开始截取字符串,默认到末尾。

String substring(int start,int end) //从指定位置开始到指定位置结束截取字符串。左闭右开

使用示例:

public class StringDemo4 {
    public static void main(String[] args) {
        // 定义一个字符串对象
        String s = "helloworld";

        // int length():获取字符串的长度。
        System.out.println("s.length:" + s.length());//10
        System.out.println("----------------------");

        // char charAt(int index):获取指定索引位置的字符
        System.out.println("charAt:" + s.charAt(7));//r
        System.out.println("----------------------");

        // int indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引。
        System.out.println("indexOf:" + s.indexOf('l'));//2
        System.out.println("----------------------");

        // int indexOf(String str):返回指定字符串在此字符串中第一次出现处的索引。
        System.out.println("indexOf:" + s.indexOf("owo"));//4
        System.out.println("----------------------");

        // int indexOf(int ch,int fromIndex):返回指定字符在此字符串中从指定位置后第一次出现处的索引。
        System.out.println("indexOf:" + s.indexOf('l', 4));//8
        System.out.println("indexOf:" + s.indexOf('k', 4)); // -1
        System.out.println("indexOf:" + s.indexOf('l', 40)); // -1
        System.out.println("----------------------");

        // int indexOf(String str,intfromIndex):返回指定字符串在此字符串中从指定位置后第一次出现处的索引。
        System.out.println("indexOf:" + s.indexOf("owo", 4));//4
        System.out.println("indexOf:" + s.indexOf("ll", 4)); //-1
        System.out.println("indexOf:" + s.indexOf("ld", 40)); // -1
        System.out.println("----------------------");

        // String substring(int start):从指定位置开始截取字符串,默认到末尾。包含start这个索引
        System.out.println("substring:" + s.substring(5));//world
        System.out.println("substring:" + s.substring(0));//helloworld
        System.out.println("----------------------");

        // String substring(int start,int
        // end):从指定位置开始到指定位置结束截取字符串。包括start索引但是不包end索引
        System.out.println("substring:" + s.substring(3, 8));//lowor
        System.out.println("substring:" + s.substring(0, s.length()));//helloworld

        /**
         * 获取 字符串中的每个字符
         */
        for(int i=0;i<s.length();i++){
            System.out.println(s.charAt(i));
        }
    }
}

转换功能

byte[] getBytes() //字符串转换为字节数组。 

char[] toCharArray() //把字符串转换为字符数组。

static String valueOf(char[] chs) //把字符数组转成字符串。

static String valueOf(int i) //把int类型的数据转成字符串。注意:String类的valueOf方法可以把任意类型的数据转成字符串。

String toLowerCase() //把字符串转成小写。

String toUpperCase() //把字符串转成大写。

String concat(String str) //把字符串拼接。

使用示例:

public class StringDemo5 {
    public static void main(String[] args) {
        // 定义一个字符串对象
        String s = "JavaSE";

        // byte[] getBytes():把字符串转换为字节数组。
        byte[] bys = s.getBytes();
        for (int x = 0; x < bys.length; x++) {
            System.out.println(bys[x]);//74 97 118 97 83 69
        }
        System.out.println("----------------");

        // char[] toCharArray():把字符串转换为字符数组。
        char[] chs = s.toCharArray();
        for (int x = 0; x < chs.length; x++) {
            System.out.println(chs[x]);//J a v a S E
        }
        System.out.println("----------------");

        // static String valueOf(char[] chs):把字符数组转成字符串。
        String ss = String.valueOf(chs);
        System.out.println(ss);//JavaSE
        System.out.println("----------------");

        // static String valueOf(int i):把int类型的数据转成字符串。
        int i = 100;
        String sss = String.valueOf(i);
        System.out.println(sss);//100 是字符串
        System.out.println("----------------");

        // String toLowerCase():把字符串转成小写。
        System.out.println("toLowerCase:" + s.toLowerCase());//javase
        System.out.println("s:" + s);
        // System.out.println("----------------");
        // String toUpperCase():把字符串转成大写。
        System.out.println("toUpperCase:" + s.toUpperCase());//JAVASE
        System.out.println("----------------");

        // String concat(String str):把字符串拼接。
        String s1 = "hello";
        String s2 = "world";
        String s3 = s1 + s2;
        String s4 = s1.concat(s2);
        System.out.println("s3:"+s3);//helloworld
        System.out.println("s4:"+s4);//helloworld
    }
}

练习:将一个字符串的首字母转成大写,其余为小写。(只考虑英文大小写字母字符)

/**
 * 需求:把一个字符串的首字母转成大写,其余为小写。(只考虑英文大小写字母字符)
 * 举例:
 * 		helloWORLD
 * 结果:
 * 		Helloworld
 *
 * 		A:先获取第一个字符
 * 		B:获取除了第一个字符以外的字符
 * 		C:把A转成大写
 * 		D:把B转成小写
 * 		E:C拼接D
 */
public class StringTest {
    public static void main(String[] args) {
        String str="helloWORLD";
        str=str.substring(0,1).toUpperCase().
                concat(str.substring(1).toLowerCase());
        System.out.println(str);
    }
}

其他功能

//替换功能:
String replace(char old,char new)

String replace(String old,String new)

//去除字符串两端空格
String trim()

//按字典顺序比较两个字符串
int compareTo(String str)
int compareToIgnoreCase(String str)

compareTo() 方法源码解析:

private final char value[]; //字符串会自动转换为一个字符数组。
/**
* 举例
  String s6 = "hello";
  String s9 = "xyz";
  System.out.println(s6.compareTo(s9));// -16
*/
public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;

    int lim = Math.min(len1, len2); //lim=3
    char v1[] = value; //v1[h e l l o]
    char v2[] = anotherString.value;//v2[x y z]

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];//h
        char c2 = v2[k];//x
        if (c1 != c2) { //先比较的是对应的字符是否相等
            return c1 - c2; //h=104  x=120   104-120=-16
        }
        k++;
    }
    //如果在指定位置,字符都相同,则长度相减
    return len1 - len2;
}

练习

练习1:把数组中的数据按照指定个格式拼接成一个字符串。举例:int[] arr = {1,2,3};输出结果:[1, 2, 3]

/**
 *
 * 需求:把数组中的数据按照指定个格式拼接成一个字符串
 * 举例:
 * 		int[] arr = {1,2,3};
 * 输出结果:
 *		"[1, 2, 3]"
 * 分析:
 * 		A:定义一个字符串对象,只不过内容为空
 * 		B:先把字符串拼接一个"["
 * 		C:遍历int数组,得到每一个元素
 * 		D:先判断该元素是否为最后一个
 * 			是:就直接拼接元素和"]"
 * 			不是:就拼接元素和逗号以及空格
 * 		E:输出拼接后的字符串
 */
public class StringTest2 {
    public static void main(String[] args) {
        int[] arr={1,2,3,4};
        System.out.println(arrayToString(arr));
    }

    /**
     * 把数组中的数据按照指定个格式拼接成一个字符串
     */
    public static String arrayToString(int[] arr){
        String str="[";
        for(int i=0;i<arr.length;i++){
            if(i==arr.length-1){
                str+=arr[i];
            }else{
                str+=arr[i]+",";
            }
        }
        str+="]";
        return str;
    }
}

练习2:统计大串中小串出现的次数

/**
 * 统计大串中小串出现的次数
 * 举例:
 * 		在字符串"woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun"
 * 结果:
 * 		java出现了5次
 *
 * 分析:
 * 		前提:是已经知道了大串和小串。
 * 		A:定义一个统计变量,初始化值是0
 * 		B:先在大串中查找一次小串第一次出现的位置 ( boolean contains(String str):判断大字符串中是否包含小字符串)
 * 			a:索引是-1,说明不存在了,就返回统计变量
 * 			b:索引不是-1,说明存在,统计变量++
 * 		C:把刚才的索引+小串的长度作为开始位置截取上一次的大串,返回一个新的字符串,并把该字符串的值重新赋值给大串
 * 		D:回到B
 */
public class StringTest3 {
    public static void main(String[] args) {
        String s1="woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun";
        String s2="java";
        System.out.println(smallCount(s2,s1));
    }

    // 统计大串中小串出现的次数
    public static int smallCount(String smallStr,String bigStr) {
        int count=0;

        int index=bigStr.indexOf(smallStr);
        while(index!=-1){
            count++;
            //把刚才的索引+小串的长度作为开始位置截取上一次的大串
            int startIndex=index+smallStr.length();
            bigStr=bigStr.substring(startIndex);
            index=bigStr.indexOf(smallStr);
        }
        return count;
    }
}

StringBuffer

常用成员方法

//StirngBuffer的构造方法
public StringBuffer() //无参构造方法

public StringBuffer(int capacity) //指定容量的字符串缓冲区对象

public StringBuffer(String str) //指定字符串内容的字符串缓冲区对象

//StringBuffer的方法
public int capacity() //返回当前容量。	理论值

public int length() //返回长度(字符数)。 实际值

使用示例:

public static void main(String[] args) {
    // public StringBuffer():无参构造方法
    StringBuffer sb = new StringBuffer();
    System.out.println("sb:" + sb);
    System.out.println("sb.capacity():" + sb.capacity()); //StringBuffer默认的指定容量是16
    System.out.println("sb.length():" + sb.length());//0
    System.out.println("--------------------------");

    // public StringBuffer(int capacity):指定容量的字符串缓冲区对象
    StringBuffer sb2 = new StringBuffer(50);
    System.out.println("sb2:" + sb2);//""
    System.out.println("sb2.capacity():" + sb2.capacity());//50
    System.out.println("sb2.length():" + sb2.length());//0
    System.out.println("--------------------------");

    // public StringBuffer(String str):指定字符串内容的字符串缓冲区对象
    StringBuffer sb3 = new StringBuffer("hello");
    System.out.println("sb3:" + sb3);//hello
    System.out.println("sb3.capacity():" + sb3.capacity());//16+5=21
    System.out.println("sb3.length():" + sb3.length());//5
}

添加功能

public StringBuffer append(String str) 
//可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身

public StringBuffer insert(int offset,String str) 
//在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身

使用示例:

public static void main(String[] args) {
    // 创建字符串缓冲区对象
    StringBuffer sb = new StringBuffer();
    
    // 链式编程
    sb.append("hello").append(true).append(12).append(34.56);
    System.out.println("sb:" + sb);

    // public StringBuffer insert(int offset,String
    // str):在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
    sb.insert(5, "world");
    System.out.println("sb:" + sb);
}

删除功能

public StringBuffer deleteCharAt(int index) 
//删除指定位置的字符,并返回本身

public StringBuffer delete(int start,int end) 
//删除从指定位置开始指定位置结束的内容,并返回本身

使用示例:

public static void main(String[] args) {
    // 创建对象
    StringBuffer sb = new StringBuffer();

    // 添加功能
    sb.append("hello").append("world").append("java");

    // public StringBuffer deleteCharAt(int index):删除指定位置的字符,并返回本身
    // 需求:我要删除e这个字符,肿么办?
    //sb.deleteCharAt(1);
    //public StringBuffer delete(int start,int end):删除从指定位置开始指定位置结束的内容,并返回本身
    // 需求:我要删除world这个字符串,肿么办?
    //sb.delete(5, 10);
    //需求:删除所有字符
    sb.delete(0,sb.length());
    System.out.println("sb:" + sb);
}

替换功能

public StringBuffer replace(int start,int end,String str) 
//从start开始到end用str替换

使用示例:

public static void main(String[] args) {
    // 创建字符串缓冲区对象
    StringBuffer sb = new StringBuffer();

    // 添加数据
    sb.append("hello");
    sb.append("world");
    sb.append("java");
    System.out.println("sb:" + sb);

    // public StringBuffer replace(int start,int end,String str):从start开始到end用str替换
    // 需求:我要把world这个数据替换为"节日快乐"
    sb.replace(5,10,"节日快乐");
    System.out.println("sb:"+sb);
}

反转功能

public StringBuffer reverse()

使用示例:

public static void main(String[] args) {
    // 创建字符串缓冲区对象
    StringBuffer sb = new StringBuffer();

    // 添加数据
    sb.append("霞青林爱我");
    System.out.println("sb:" + sb);//霞青林爱我

    // public StringBuffer reverse()
    sb.reverse();
    System.out.println("sb:" + sb);//我爱林青霞
}

截取功能

public String substring(int start) // 注意截取返回的是String,而不是StringBuffer了

public String substring(int start,int end)

使用示例:

public static void main(String[] args) {
    // 创建字符串缓冲区对象
    StringBuffer sb = new StringBuffer();

    // 添加元素
    sb.append("hello").append("world").append("java");
    System.out.println("sb:" + sb);//sb:helloworldjava

    // 截取功能
    // public String substring(int start)
    String s = sb.substring(5);
    System.out.println("s:" + s);//s:worldjava
    System.out.println("sb:" + sb);//sb:helloworldjava

    // public String substring(int start,int end)
    String ss = sb.substring(5, 10);//左闭右开
    System.out.println("ss:" + ss);//ss:world
    System.out.println("sb:" + sb);//sb:helloworldjava
}

String 和 StringBuffer 的相互转换

public class StringBufferDemo7 {
    public static void main(String[] args) {
        String s="hello";
        System.out.println(stringToStringBuffer(s)); //hello
        System.out.println(stringBufferToString(stringToStringBuffer(s)));//hello
    }

    // String -->StringBuffer
    public static StringBuffer stringToStringBuffer(String s) {
        // 注意:不能把字符串的值直接赋值给StringBuffer
        // StringBuffer sb = "hello";
        // StringBuffer sb = s;

        // 方式1:通过构造方法
      /*  StringBuffer sb = new StringBuffer(s);
        return sb;*/
        // 方式2:通过append()方法
        StringBuffer sb2 = new StringBuffer();
        sb2.append(s);
        return sb2;
    }

    //StringBuffer -->String
    public static String stringBufferToString(StringBuffer buffer){
        // 方式1:通过构造方法
       /* String str = new String(buffer);
        return str;*/
        // 方式2:通过toString()方法
        String str2 = buffer.toString();
        return str2;
    }
}

练习

练习1:将数组拼接成一个字符串

public class StringBufferTest {
    public static void main(String[] args) {
        int[] arr={1,2,3,4};
        System.out.println(arrToString(arr));//[1,2,3,4]
    }

    public static String arrToString(int[] arr){
        StringBuffer buffer=new StringBuffer();
        buffer.append("[");
        for(int i=0;i<arr.length;i++){
            if(i==arr.length-1){
                buffer.append(arr[i]);
            }else{
                buffer.append(arr[i]).append(",");
            }
        }
        buffer.append("]");
        return buffer.toString();
    }
}

练习2:判断一个字符串是否是对称字符串

/**
 *  判断一个字符串是否是对称字符串
 * 例如"abc"不是对称字符串,"aba"、"abba"、"aaa"、"mnanm"是对称字符串
 *
 * 分析:
 * 		判断一个字符串是否是对称的字符串,我只需要把
 * 			第一个和最后一个比较
 * 			第二个和倒数第二个比较
 * 			...
 * 		比较的次数是长度除以2。
 */
public class StringBufferTest2 {
    public static void main(String[] args) {
        System.out.println(isSymmetry("aba")); //true
        System.out.println(isSymmetry2("aabbaa"));//true
    }

    //通过字符数组的方式来比较
    public static boolean isSymmetry(String s){
        boolean flag=true;
        char[] chs=s.toCharArray();
        int len=chs.length;
        for(int start=0,end=chs.length-1;start<=end;start++,end--){
            if(chs[start]!=chs[end]){
                flag=false;
                break;
            }
        }
        return flag;
    }

     //通过StringBuffer的reverse
    public static boolean isSymmetry2(String s){
        //通过StringBuffer的reverse获取反转字符串s2
        String s2=new StringBuffer(s).reverse().toString();
        return s2.equals(s);
    }
}

练习3:看程序写结果

public class StringBufferTest3 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "world";
        System.out.println(s1 + "---" + s2);
        change(s1, s2);
        System.out.println(s1 + "---" + s2);

        StringBuffer sb1 = new StringBuffer("hello");
        StringBuffer sb2 = new StringBuffer("world");
        System.out.println(sb1 + "---" + sb2);
        change(sb1, sb2);
        System.out.println(sb1 + "---" + sb2);
    }

    //StringBuffer作为参数传递
    public static void change(StringBuffer sb1, StringBuffer sb2) {
        sb1 = sb2;
        sb2.append(sb1);
    }

    //String作为参数传递
    public static void change(String s1, String s2) {
        s1 = s2;
        s2 = s1 + s2;
    }
}

输出结果:

hello---world
hello---world
hello---world
hello---worldworld

String作为参数传递,效果和基本类型作为参数传递是一样的。

String, StringBuffer and StringBuilder

1. 可变性

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

2. 线程安全

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

3. 性能

  • Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢
  • StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用
  • StringBuilder每次都会对StringBuilder对象本身进行操作,而不是生成新的对象并改变对象引用。
    相同情况下使用StirngBuilder相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却冒多线程不安全的风险。

三者使用的总结

  • 操作少量的数据,使用String
  • 单线程操作字符串缓冲区下操作大量数据,使用StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据,使用StringBuffer

运算

基本运算

== 和 equals()

  • == 判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。

    • 基本数据类型:== 比较的是值
    • 引用数据类型:== 比较的是内存地址
  • equals() 判断两个对象是否相等。但它一般有两种使用情况:

    • 情况1:类没有重写 equals() 方法。等价于“==”。
    • 情况2:类重写了 equals() 方法。一般用来比较两个对象的内容,若它们的内容相等,则返回 true (即,认为这两个对象相等)。

注意:

  • String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址, 而 String 的 equals 方法比较的是对象的值。

  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。
    如果没有就在常量池中重新创建一个 String 对象。

public class EqualsDemo {
    public static void main(String[] args) {
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b为另一个引用,对象的内容一样
        String aa = "ab"; // 放在常量池中
        String bb = "ab"; // 从常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}

参数传递

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

以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。
在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中
因此在方法中使指针引用其它对象,那么这两个指针此时指向的是完全不同的对象,在一方改变其所指向对象的内容时对另一方没有影响。

public class Dog {

    String name;

    Dog(String name) {
        this.name = name;
    }

    String getName() {
        return this.name;
    }

    void setName(String name) {
        this.name = name;
    }

    String getObjectAddress() {
        return super.toString();
    }
}
public class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        func(dog);
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        System.out.println(dog.getName());          // A
    }

    private static void func(Dog dog) {
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        dog = new Dog("B");
        System.out.println(dog.getObjectAddress()); // Dog@74a14482
        System.out.println(dog.getName());          // B
    }
}

如果在方法中改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。

class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        func(dog);
        System.out.println(dog.getName());          // B
    }

    private static void func(Dog dog) {
        dog.setName("B");
    }
}

float 与 double

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

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

// float f = 1.1;

1.1f 字面量才是 float 类型。

float f = 1.1f;

隐式类型转换

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

short s1 = 1;
// s1 = s1 + 1;

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

s1 += 1;
// s1++;

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

s1 = (short) (s1 + 1);

switch

从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。

String s = "a";
switch (s) {
    case "a":
        System.out.println("aaa");
        break;
    case "b":
        System.out.println("bbb");
        break;
}

switch 不支持 long,是因为 switch 的设计初衷是对那些只有少数的几个值进行等值判断,如果值过于复杂,
那么还是用 if 比较合适。

// long x = 111;
// switch (x) { 
// Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'
//     case 111:
//         System.out.println(111);
//         break;
//     case 222:
//         System.out.println(222);
//         break;
// }

Java正则表达式

正则表达式

概述

是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。

其实就是一种规则

组成规则

规则字符在java.util.regex Pattern类中:Pattern API

常见组成规则

1. 字符

元字符说明
x字符 x
\反斜线字符
\n新行(换行)符 (‘\u000A’)
\r回车符 (‘\u000D’)

2. 字符类

元字符说明
[abc]a、b 或 c(简单类)
[^abc]任何字符,除了 a、b 或 c(否定)
[a-zA-Z]a到 z 或 A到 Z,两头的字母包括在内(范围)
[0-9]0到9的字符都包括

3. 预定义字符类

元字符说明
.任何字符
\d数字。等价于[0-9]
\w单词字符。等待雨[a-zA-Z_0-9]

4. 边界匹配器

元字符说明
^行的开头
$行的结尾
\b单词边界

5. 数量词

元字符说明
X?X,零次或一次
X*X,零次或多次
X+X,一次或多次
X{n}X,恰好 n 次
X{n,}X,至少 n 次
X{n,m}X,至少 n 次,但是不超过 m 次

应用

判断功能

String 类 matches 方法

public boolean matches(String regex)
/**
* 判断手机号码是否满足要求
*/
public static void main(String[] args) {
    //键盘录入手机号码
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入你的手机号码:");
    String phone = sc.nextLine();

    //定义手机号码的规则
    String regex = "1[38]\\d{9}";

    //调用功能,判断即可
    boolean flag = phone.matches(regex);

    //输出结果
    System.out.println("flag:"+flag);
}

分割功能

String类的 split 方法

public String[] split(String regex)
/**
* 根据给定正则表达式的匹配拆分此字符串
*/
public static void main(String[] args) {
    //定义一个年龄搜索范围
    String ages = "18-24";

    //定义规则
    String regex = "-";

    //调用方法
    String[] strArray = ages.split(regex);
    int startAge = Integer.parseInt(strArray[0]);
    int endAge = Integer.parseInt(strArray[1]);
}

替换功能

String 类的 replaceAll 方法

public String replaceAll(String regex,String replacement)
/**
* 去除所有的数字
*/
public static void main(String[] args) {
    // 定义一个字符串
    String s = "helloqq12345worldkh622112345678java";


    // 直接把数字干掉
    String regex = "\\d+";
    String ss = "";

    String result = s.replaceAll(regex, ss);
    System.out.println(result);
}

获取功能

Pattern和Matcher类的使用

/**
* 模式和匹配器的基本顺序
*/
public static void main(String[] args) {
    // 模式和匹配器的典型调用顺序
    // 把正则表达式编译成模式对象
    Pattern p = Pattern.compile("a*b");
    // 通过模式对象得到匹配器对象,这个时候需要的是被匹配的字符串
    Matcher m = p.matcher("aaaaab");
    // 调用匹配器对象的功能
    boolean b = m.matches();
    System.out.println(b);

    //这个是判断功能,但是如果做判断,这样做就有点麻烦了,我们直接用字符串的方法做
    String s = "aaaaab";
    String regex = "a*b";
    boolean bb = s.matches(regex);
    System.out.println(bb);
}

End

这里是结束!!!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值