学习JavaEE的日子 Day19 常用类

Day19

1.包装类的使用

理解:8种基本数据类型对应类

出现原因:

​ Java为纯面向对象语言(万物皆对象),8种基本数据类型不能new对象,

​ 就破坏Java为纯面向对应语言的特征,Java又为8种基本数据类型分别

​ 匹配了对应的类,这种类叫做包装类/封装类

8种基本数据类型对应包装类
基本数据类型包装类继承关系
byteByteObject.Number.Byte
shortShortObject.Number.Short
intIntegerObject.Number.Integer
longLongObject.Number.Long
floatFloatObject.Number.Float
doubleDoubleObject.Number.Double
charCharacterObject.Character
booleanBooleanObject.Boolean

注意:

​ 1.int的包装类的写法为Integer

​ 2.char的包装类的写法是:Character

​ 3.其余基本数据类型的包装类均是基本类型的首字母大写。

1.1 手动装箱

手动装箱:基本数据类型 -> 包装类

  int num = 100;
  Integer integer = Integer.valueOf(num);
  System.out.println(integer);  //打印出来是100,这个是字符串(String类型)

1.2 自动拆箱

手动拆箱:包装类 -> 基本数据类型

   Integer integer = new Integer(100);
   int num = integer.intValue();
   System.out.println(num);  //打印出来是100,这个是int类型

1.3 自动拆装箱

JDK1.5开始的新特性:自动拆装箱

自动装箱:基本数据类型 -> 包装类

  int num = 100;
  Integer integer = num;//底层实现:Integer.valueOf(num);
  System.out.println(integer);

自动拆箱:包装类 -> 基本数据类型

  Integer integer = new Integer(100);
  int num = integer;//底层实现:integer.intValue(); JVM自动封装
  System.out.println(num);

//将字符串转换为int值

  String str = "123";     //String str = "123aaaa"; 直接报错
  int num = Integer.parseInt(str);
  System.out.println(num);

1.4 深入Integer类的底层原理

面试题:描述Integer的valueOf底层实现原理

答:判断输入的int值是否在-128~127的区间内,

​ 如果在就从Integer的缓存类的缓存数组中获取对象,

​ 如果不在,就重新new Integer的对象

public class Test02 {
	public static void main(String[] args) {
		
		//面试题:描述下列代码的运行结果
		Integer integer1 = Integer.valueOf(100);
		Integer integer2 = Integer.valueOf(100);
		System.out.println(integer1 == integer2);//true   // ==  两个对象比较,不是100在比较,而是比较的是内存地址
		
		Integer integer3 = Integer.valueOf(200);
		Integer integer4 = Integer.valueOf(200);
		System.out.println(integer3 == integer4);//false
		
		System.out.println("---------------------");
		//面试题解释如下:
//		Integer integer = new Integer(100);
//		System.out.println(integer);//100
//		int num1 = integer.intValue();
//		System.out.println(num1);//100
//		
//		MyInteger myInteger = new MyInteger(100);
//		System.out.println(myInteger);//100,不写下面的myInteger类的toString方法,就会打印全类名
//		int num2 = myInteger.intValue();
//		System.out.println(num2);//100
		
		MyInteger myInteger1 = MyInteger.valueOf(100);
		MyInteger myInteger2 = MyInteger.valueOf(100);
		System.out.println(myInteger1 == myInteger2);//true
		
		MyInteger myInteger3 = MyInteger.valueOf(200);
		MyInteger myInteger4 = MyInteger.valueOf(200);
		System.out.println(myInteger3 == myInteger4);//false ( 200 不在-128,127中,就去new一个新的对象)

	}
}

手写Integer方法,看看底层代码

public class MyInteger {

	private int value;//将值传入到value中

	public MyInteger(int value) {
		this.value = value;
	}
	
	public int intValue(){
		return value;
	}
	
	//手动装箱
	public static MyInteger valueOf(int i){
        //判断 i 在不在缓存数组中(-128,127)
		if(i>=MyIntegerCache.low && i<=MyIntegerCache.high){
            //在,就把下标取出来
			return MyIntegerCache.cache[i + (-MyIntegerCache.low)];
		}
        //如果不在-128,127中,就去new一个新的对象
		return new MyInteger(i);
	}
	
	@Override
	public String toString() {
		return String.valueOf(value);//将int值转换为字符串
	}
	
	//缓存类(内部类),设置private,不想让外部调用     
	private static class MyIntegerCache{
		
		private static final int low = -128;//低位
		private static final int high = 127;//高位
		private static final MyInteger[] cache;//缓存数组(-128~127的MyInteger的对象)
		
        //静态代码块
		static{
			//初始化缓存数组
			cache = new MyInteger[high - low + 1];//256
			int j = low;
			for (int i = 0; i < cache.length; i++) {
				cache[i] = new MyInteger(j++);
			}
		}
		
	}
}

2.关于字符串类

分类:

​ 1.String

​ 2.StringBuffer

​ 3.StringBuilder

2.1 String的使用

理解:String是****不可变****类, 即一旦一个String对象被创建, 包含在这个对象中的字符序列是不可改变的, 直至该对象被销毁。

​ String类是final类,不能有子类。

注意:字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )

2.1.1 获得新字符串的常用方法(拼接+替换)

String concat(String str) 拼接字符串,并返回新的字符串

String subString(int beginIndex) 从开始下标处beginIndex截取到末尾,并返回新的字符串

public String toLowerCase() 转小写,并返回新的字符串

public String toUpperCase() 转大写,并返回新的字符串

public String trim() 去除首尾空格,并返回新的字符串

public String replace(char oldChar, char newChar) 替换字符,并返回新的字符串

public String replace(CharSequence target, CharSequence replacement) 替换字符串,并返回新的字符串

public String replaceAll(String regex, String replacement) 替换字符串,并返回新的字符串

public replaceFirst(String regex, String replacement) 替换第一次出现的字符串,并返回新的字符串

2.1.2 计算字符串长度的方法

public int length(); 返回字符串字符的个数。(从1开始)

2.1.3 字符串比较

equals() 比较两个字符串内容是否相同(区分大小写)(字符串类对此方法进行了重写)

equalsIgnoreCase() 比较两个字符串内容是否相同(不区分大小写)

== 比较两个字符串是否为同一对象,即在内存中的地址是否一样

startsWith() 判断字符串是否以某个字符串开头

endsWith() 判断字符串是否以某个字符串结尾

2.1.4 字符串查找

charAt(char ch) 获取字符串中指定下标的字符

indexOf(String str) 查找子字符串在此字符串第一次出现的下标,如果没找到,则返回 -1

lastIndexOf(String str) 查找子字符串在此字符串最后一次出现的下标

2.1.5 其他数据类型转成字符串

在String类中定义了一些静态的重载方法

public static String valueOf(…)可以将基本类型数据、Object类型转换为字符串。如:

​ public static String valueOf(double d) 把double类型数据转成字符串

​ public static String valueOf(Object obj) 调用obj的toString()方法得到它的字符串表示形式。

其他类型 + “”:加一个空字符串也是可以转成字符串

public class Test01 {
	public static void main(String[] args) {
		
		String str = "123abc";//底层:new char[]{'1','2','3','a','b','c'};
		
		str = str.concat("DEF123");//拼接字符串,并返回新的字符串
		str = str.substring(2);//从开始下标处截取到末尾,并返回新的字符串
		str = str.substring(1, 7);//从开始下标处(包含)截取到结束下标处(排他),并返回新的字符串

		str = str.toUpperCase();//转大写,并返回新的字符串
		str = str.toLowerCase();//转小写,并返回新的字符串
		
		str = "   123  abc DEF   123         ";
		
		str = str.trim();//去除首尾空格,并返回新的字符串
		str = str.replace('a', 'A');//替换字符,并返回新的字符串
		str = str.replace("Abc", "ABC");//替换字符串,并返回新的字符串
		str = str.replaceAll("ABC", "abc");//替换字符串,并返回新的字符串
		str = str.replaceFirst("23", "66");//替换第一次出现的字符串,并返回新的字符串
		str = str.replaceAll(" ", "");//去除空格(将空格字符串替换成空内容的字符串)
		
		int len = str.length();
		System.out.println("获取字符串的字符个数:" + len);
		
		System.out.println("判断两个字符串内容是否相同(区分大小写):" + str.equals("166abcDEF123"));//true
		System.out.println("判断两个字符串内容是否相同(不区分大小写):" + str.equalsIgnoreCase("166ABCdef123"));//true
		System.out.println("判断字符串是否以某个字符串开头:" + str.startsWith("166a"));//true
		System.out.println("判断字符串是否以某个字符串结尾:" + str.endsWith("DEF123"));//true
		
		System.out.println("查找子字符串在此字符串第一次出现的下标:" + str.indexOf("1"));//0
		System.out.println("查找子字符串在此字符串最后一次出现的下标:" + str.lastIndexOf("1"));//9
		System.out.println("获取字符串中指定下标的字符:" + str.charAt(6));//D
		
		//166abcDEF123
		System.out.println(str);
		
		System.out.println("-------------------------");
		
		//把其他类型转换为String类型
		System.out.println(String.valueOf(10));//int -> String
		System.out.println(String.valueOf(11.11));//double -> String
		System.out.println(String.valueOf(true));//boolean -> String
		System.out.println(String.valueOf('A'));//char -> String
		
		System.out.println("-------------------------");
		
		//把其他类型转换为String类型
		System.out.println(10 + "");//int -> String
		System.out.println(11.11 + "");//double -> String
		System.out.println(true + "");//boolean -> String
		System.out.println('A' + "");//char -> String

	}
}

String练习题:
练习:完成一个邮箱格式的校验 hhy@qq.com
(1),“@”不能在第一位
(2),“.”不能在最后一位
(3),“@”和“.”中间应该有字符
(4),@.***
当然还有更严格的校验规则,我们此处考虑到这即可

	 String email = "hhy@qq.com";
		 
	 int indexOf1 = email.indexOf("@");
	 int indexOf2 = email.indexOf(".");
		 
	 if(indexOf1==0 || indexOf2==email.length()-1 || indexOf2-indexOf1<=1){
		 System.out.println("邮箱格式错误");
	 }else{
		 System.out.println("邮箱格式正确");
   }

注意:==号的作用

比较基本数据类型:比较的是具体的值

比较引用数据类型:比较的是对象地址值

2.2 StringBuffer

StringBuffer代表****可变****的字符序列。

StringBuffer称为字符串缓冲区,它的工作原理是:预先申请一块内存,存放字符序列,如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列。

StringBuffer是可变对象,这个是String最大的不同

StringBuffer可以理解为一个字符串容器,可以动态的改变容器中的内容。

2.2.1 StringBuffer常用方法

append() 在末尾追加字符串

insert(int offset,String str) 将子字符串插入到此字符串指定下标的位置

setCharAt(int offset,char ch) 替换指定下标上的字符

replace(int start,int end,String str) //从开始下标处(包含)替换到结束下标处(排他)的字符串

deleteCharAt(int index) 删除指定下标上的字符

delete(int start,int end) 从开始下标处(包含)删除到结束下标处(排他)的字符串

reverse() 反转字符串

length() 确定Stringbuffer字符串的长度

public class Test02 {
	public static void main(String[] args) {
		
		StringBuffer sb = new StringBuffer();
		
		sb.append("123abc");//在末尾追加字符串
		sb.append("DEF123");//在末尾追加字符串
		
		sb.insert(6, "xyz");//将子字符串插入到此字符串指定下标的位置
		sb.setCharAt(6, 'X');//替换指定下标上的字符
		sb.replace(3, 9, "ABCABC");//从开始下标处(包含)替换到结束下标处(排他)的字符串
		
		sb.deleteCharAt(6);//删除指定下标上的字符
		sb.delete(3, 11);//从开始下标处(包含)删除到结束下标处(排他)的字符串
		
		sb.reverse();//反转字符串
		
		int len = sb.length();
		System.out.println("字符串长度为:" + len);
		
		//321321
		System.out.println(sb.toString());
	}
}

2.3 StringBuilder

StringBuilder与StringBuffer的用法完全一致,唯一的区别是StringBuffer是线程安全的,而StringBuilder不是线程安全的。所以StringBuilder的性能要比StringBuffer要好。

单线程推荐使用StringBuilder,多线程使用StringBuffer。

2.3.1 StringBuilder常用方法

注意:在调用方法的层面上StringBuilder和StringBuffere是一样的

2.3.2 为什么需要StringBuilder及StringBuffer类

字符串的不变性:

一个String对象的长度是固定的,不能改变它的内容,或者是附加新的字符到String对象中。

您也许会使用+来串联字符串以达到附加新字符或字符串的目的,但+会产生一个新的String对象。

String s1 = “abc”;

String s2 = “abc”;

String s3 = s1+s2;

如果程序对这种附加字符串的需求很频繁,系统会频繁在内存中创建String对象,造成性能下降。所以并不建议使用+来进行频繁的字符串串联。应该使用java.lang.StringBuffer类。

public class Test03 {
	public static void main(String[] args) {
		
		StringBuilder sb = new StringBuilder();
		
		sb.append("123abc");//在末尾追加字符串
		sb.append("DEF123");//在末尾追加字符串
		
		sb.insert(6, "xyz");//将子字符串插入到此字符串指定下标的位置
		sb.setCharAt(6, 'X');//替换指定下标上的字符
		sb.replace(3, 9, "ABCABC");//从开始下标处(包含)替换到结束下标处(排他)的字符串
		
		sb.deleteCharAt(6);//删除指定下标上的字符
		sb.delete(3, 11);//从开始下标处(包含)删除到结束下标处(排他)的字符串
		
		sb.reverse();//反转字符串
		
		int len = sb.length();
		System.out.println("字符串长度为:" + len);
		
		//321321
		System.out.println(sb.toString());
	}
}

2.4 深入String – 面试题

常量池概念:

​ Java运行时会维护一个常量池, 常量池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。

public class Test04 {
	public static void main(String[] args) {
		
		//面试题:描述下列代码会创建几个String对象?
		//答案:1个(常量池中的数据不允许重复)
        //没有new对象,
        //只是记录字面值常量会在常量池中开辟一个对象:abc 0x01
        //所以只有1个
//		String str1 = "abc";
//		String str2 = "abc";
//		System.out.println(str1);
//		System.out.println(str2);
		
		//面试题:描述下列代码会创建几个String对象?
		//答案:3个(new了两个String对象 + "abc")
        //new 对象 会在堆中开辟内存空间:str1 0x001,str2 0x002
        //字面值常量会在常量池中开辟一个对象:abc 0x01
        //所以有3个
		String str1 = new String("abc");
		String str2 = new String("abc");
		System.out.println(str1);
		System.out.println(str2);
	}
}

关于字符串的小扩展:

1.字符串存储的内存原理

String s = “abc”;直接赋值

特点:

​ 此时字符串abc是存在字符串常量池中的。

​ 先检查字符串常量池中有没有字符串abc,如果有,不会创建新的,而是直接复用。如果没有abc,才会创建一个新的。

​ 所以,直接赋值的方式,代码简单,而且节约内存。

2.new出来的字符串

看到new关键字,一定是在堆里面开辟了一个小空间。

String s1 = new String(“abc”);

String s2 = “abc”;

s1记录的是new出来的,在堆里面的地址值。

s2是直接赋值的,所以记录的是字符串常量池中的地址值

2.5 深入String创建对象

两个常量在编译时直接拼接

两个常量在编译时直接拼接

有变量参与字符串拼接的情况,底层会创建StringBuilder进行拼接

​ new StringBuilder(String.value(s3)).append(s4).toString(); --> 先创建StringBuilder对象,再进行拼接

public class Test05 {
	public static void main(String[] args) {
		
		String str1 = "abc";
		String str2 = "abc";
		System.out.println(str1 == str2);//true
		
		//注意:两个常量在编译时直接拼接
		String str3 = "ab" + "c";//String str3 = "abc";
		System.out.println(str1 == str3);//true
		
		//注意:两个常量在编译时直接拼接
		final String s1 = "ab";   //常量
		final String s2 = "c";    //常量
		String str4 = s1 + s2;//String str3 = "abc";
		System.out.println(str1 == str4);
		
		//注意:有变量参与字符串拼接的情况,底层会创建StringBuilder进行拼接
		String s3 = "ab";  //变量
		String s4 = "c";  //变量
		String str5 = s3+s4;//new StringBuilder(String.value(s3)).append(s4).toString();
		System.out.println(str1 == str5);//false
	}
}

注意:

​ 常量:在程序执行过程中不可发生改变的量

​ 常量的分类:数字字面量、字面值常量、final修饰的变量

2.6.1 创建字符串对象两种方式的区别

通过构造方法创建

​ 通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同直接赋值方式创建

以“ ”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护

2.6 StringBuilder 和 StringBuffer的底层原理

注意:

​ 1.StringBuffer和StringBuilder都是继承的AbstractStringBuilder

​ 2.StringBuffer和StringBuilder的核心功能都是由父类使用

​ 3.StringBuffer在方法中上锁、解锁 – 在多线程下使用

​ 4.StringBuffer和StringBuilder相比,因为有了上锁和解锁的步骤,所以效率没有StringBuilder高

public class Test06 {
	public static void main(String[] args) {
		
		//StringBuilder的功能
		
		StringBuffer sb = new StringBuffer();
		sb.append("xxx");
		sb.append("yyy");
		sb.append("zzz");
		System.out.println(sb);		
		
		//StringBuilder的功能
//		StringBuilder sb = new StringBuilder();
//		sb.append("xxx");
//		sb.append("yyy");
//		sb.append("zzz");
//		System.out.println(sb);		
	}
}

AbstractStringBuilder底层代码

import java.util.Arrays;

abstract class MyAbstractStringBuilder {

	//缓存数组
	char[] value;
	
	//存储字符个数
	int count;
	
	MyAbstractStringBuilder() {
    }

	MyAbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
	
	//获取字符个数
    public int length() {
        return count;
    }
    
    public MyAbstractStringBuilder append(String str) {
    	
    	//如果添加的字符串为null
        if (str == null)
            return appendNull();
        
        
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    
    //添加字符串为null的情况
    private MyAbstractStringBuilder appendNull() {
        int c = count;
        
        //判断是否扩容的方法
        ensureCapacityInternal(c + 4);//4 - "null".length()
        
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }
    
    //判断是否扩容
    private void ensureCapacityInternal(int minimumCapacity) {
        // 有溢出意识的代码(容量必须大于缓存数组的长度才扩容)
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,newCapacity(minimumCapacity));
        }
    }
    
    //计算新的长度
    private int newCapacity(int minCapacity) {
        // 扩容机制:原来的长度*2 + 2
        int newCapacity = (value.length << 1) + 2;//为什么+2的原因:怕用户设置初始化数组长度为0,0<<1还是0,就得不到扩容
        return newCapacity;
    }
    
}

StringBuilder底层代码

public class MyStringBuilder extends MyAbstractStringBuilder{

	public MyStringBuilder() {
        super(16);//默认缓冲数组长度为16
    }
	
	public MyStringBuilder(int capacity) {
        super(capacity);//自定义缓冲数组的长度
    }
	
	public MyStringBuilder(String str) {
        super(str.length() + 16);//自定义缓冲数组的长度:字符串长度+16
        append(str);//追加字符串
    }
	
	@Override
    public MyStringBuilder append(String str) {
        super.append(str);//依赖于父类的append
        return this;
    }
}

StringBuffer底层代码

public class MyStringBuffered extends MyAbstractStringBuilder{

	public MyStringBuffered() {
        super(16);//默认缓冲数组长度为16
    }
	
	public MyStringBuffered(int capacity) {
        super(capacity);//自定义缓冲数组的长度
    }
	
	public MyStringBuffered(String str) {
        super(str.length() + 16);//自定义缓冲数组的长度:字符串长度+16
        append(str);//追加字符串
    }
	
	//synchronized - 线程安全的方法
	@Override
    public synchronized  MyStringBuffered append(String str) {//上锁
        super.append(str);//依赖于父类的append
        return this;
    }//解锁
}

2.7 StringBuilder 和 StringBuffer的构造方法

StringBuilder/StringBuffer代表可变的字符序列

StringBuilder/StringBuffer称为字符串缓冲区

它的工作原理是:预先申请一块内存(char[] value = new char[]),存放字符序列,
如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列(扩容机制:原来数组长度的两倍+2)。

StringBuilder/StringBuffer是可变对象:因为char[] value = new char[];
String是不可变的对象,因为final char[] value = new char[];

public class Test07 {
	public static void main(String[] args) {
		
		//缓冲区默认长度:16个字符
		//StringBuffer sb = new StringBuffer();

		//自定义缓冲长度:100个字符
		//StringBuffer sb = new StringBuffer(100);
		
		//自定义缓冲长度:"abc".length() + 16
//		StringBuffer sb = new StringBuffer("abc");
//		System.out.println(sb);
		
		System.out.println("-------------");
		
		//缓冲区默认长度:16个字符
		//StringBuilder sb = new StringBuilder();

		//自定义缓冲长度:100个字符
		//StringBuilder sb = new StringBuilder(100);
		
		//自定义缓冲长度:"abc".length() + 16
//		StringBuilder sb = new StringBuilder("abc");
//		System.out.println(sb);
			
		System.out.println("-------------");
		
		//底层有两个字符串:“abc”,"abcd"
		String str = "abc";
		str = "abcd";
		System.out.println(str);
		
		//底层只有一个缓存数组
		StringBuilder sb = new StringBuilder();
		sb.append("abc");
		sb.append("c");
		System.out.println(sb);
	}
}

2.8 频繁拼接字符串,使用StringBuilder/StringBuffer

public class Test08 {
	public static void main(String[] args) {
		
		//获取自1970年1月1日0:0:0 到现在的毫秒数(1000毫秒 = 1秒)
		//long currentTimeMillis = System.currentTimeMillis();
		
//		long startTime = System.currentTimeMillis();
//		String str = "莹莹";
//		for (int i = 1; i < 50000; i++) {
//			str += "小可爱,皇冠给你带";
			//底层实现:
			//	str = str + "小可爱,皇冠给你带";
			//	str = new StringBuidler(str).append("小可爱,皇冠给你带");
//		}
//		long endTime = System.currentTimeMillis();
//		System.out.println("消耗时长:" + (endTime-startTime));//6640
		
		System.out.println("--------------------------------------------");
		
//		long startTime = System.currentTimeMillis();
//		StringBuffer sb = new StringBuffer("莹莹");
//		for (int i = 1; i < 50000; i++) {
//			sb.append("小可爱,皇冠给你带");
//		}
//		long endTime = System.currentTimeMillis();
//		System.out.println("消耗时长:" + (endTime-startTime));//5
		
		System.out.println("--------------------------------------------");
		
		long startTime = System.currentTimeMillis();
		StringBuffer sb = new StringBuffer(450060);//优化方案:减少底层扩容次数
		sb.append("莹莹");
		for (int i = 1; i < 50000; i++) {
			sb.append("小可爱,皇冠给你带");
		}
		long endTime = System.currentTimeMillis();
		System.out.println("消耗时长:" + (endTime-startTime));//5
	}
}

3.正则表达式

理解:符合某个语句规范的字符串

逻辑运算符

&&:并且

| :或者

\ :转义字符

预定义字符

“.” : 匹配任何字符。

“\d”:任何数字[0-9]的简写;

“\D”:任何非数字[^0-9]的简写;

“\s”: 空白字符:[ \t\n\x0B\f\r] 的简写

“\S”: 非空白字符:[^\s] 的简写

“\w”:单词字符:[a-zA-Z_0-9]的简写

“\W”:非单词字符:[^\w]

预定义字符

“.” : 匹配任何字符。

“\d”:任何数字[0-9]的简写;

“\D”:任何非数字[^0-9]的简写;

“\s”: 空白字符:[ \t\n\x0B\f\r] 的简写

“\S”: 非空白字符:[^\s] 的简写

“\w”:单词字符:[a-zA-Z_0-9]的简写

“\W”:非单词字符:[^\w]

分组括号( )

只看左括号,不看有括号,按照左括号的顺序,从左往右,依次为第一组,第二组,第三组等等

3.1 利用正则表达式做替换

案例:把一个字符串中带电话号码替换成130****1111

public class Test01 {
	public static void main(String[] args) {
		
		String str = "小红13012341111 小绿15112342222 小黑13912343333";
		
		String regex = "(1\\d{2})(\\d{4})(\\d{4})";
		
		str = str.replaceAll(regex, "$1****$3");//$n保留第几组正则表达式里的数据
		System.out.println(str);
		
	}
}	

3.2 利用正则表达式做验证

案例:校验QQ邮箱

public class Test02 {
	public static void main(String[] args) {
		
		String email = "1445584980@qq.com";
		
		String regex = "\\d{4,11}@qq.com";
		
		boolean matches = email.matches(regex);
		System.out.println(matches);
		
	}
}	

3.3 利用正则表达式做字符串的分割

案例:分隔路径

public class Test03 {
	public static void main(String[] args) {
		
		String str = "C:\\资源\\日韩\\波多野结衣.avi";
		
		//:?表示冒号出现1次或0次
		String regex = ":?\\\\";//分割  :\\ 或 \\
		
		String[] split = str.split(regex);
		for (String string : split) {
			System.out.println(string);
		}
		
	}
}	

3.4 利用正则表达式做字符串的爬数据的工作

Pattern:表示正则表达式

Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取。

​ 在大串中去找符合匹配规则的子串。

案例:Pattern+Matcher 找到前端代码中的图片路径

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test04 {
	public static void main(String[] args) {
		
		String str = "<img src='hhy/aaa.jpg'/><div><div/> <input type='image' src='submit.gif' /><img src='bbb.jpg'/>";
		
		String regex = "<img\\b[^>]*\\bsrc\\b\\s*=\\s*('|\")?([^'\"\n\r\f>]+(\\.jpg)\\b)[^>]*>";
		
		//获取正则表达式对象
		Pattern pattern = Pattern.compile(regex);
		//获取匹配结果
		Matcher matcher = pattern.matcher(str);
		
		//循环遍历匹配结果
		while(matcher.find()){
			//获取匹配结果
			String group = matcher.group(2);//2表示第2组数据
			System.out.println(group);
		}
		
	}
}	

4.日期时间类

Date(java.util) – 日期类

SimpleDateFormat – 格式化日期类

Calendar – 日历类

4.1 Date 日期类

java.util.Date类表示特定的瞬间,精确到毫秒

public class Test01 {
	public static void main(String[] args) {
		
		Date date = new Date();
		
		//星期 月份 日期 时:分:秒 时区 年份
		//Sat Jan 27 14:31:41 CST 2024
		System.out.println(date);
	}
}

4.2 SimpleDateFormat 格式化日期类

将显示的日期信息格式化

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Test02 {
	public static void main(String[] args) throws ParseException {
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
		
		//Date 转 String
		String datetime = sdf.format(new Date());
		System.out.println(datetime);
		
		//String 转 Date 
		Date date = sdf.parse("2024年01月27日 14时37分61秒");
		System.out.println(date);
	}
}

4.3 Calendar 日历类

Calendar类(日历)是一个抽象基类,主要用于完成日期字段之间相互操作的功能。即可以设置和获取日期数据的特定部分。

import java.util.Calendar;

public class Test03 {
	public static void main(String[] args) {
		
		//获取当前系统的日历对象
		Calendar c = Calendar.getInstance();
	
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH)+1;//注意:月份0~11,0表示1月,所以加个1
		int day = c.get(Calendar.DAY_OF_MONTH);
		int hour = c.get(Calendar.HOUR);
		int minute = c.get(Calendar.MINUTE);
		int second = c.get(Calendar.SECOND);
		
		System.out.println(year);
		System.out.println(month);
		System.out.println(day);
		System.out.println(hour);
		System.out.println(minute);
		System.out.println(second);
	}
}

小结:

​ Date + SimpleDateFormat一起使用,获取我们想要的日期时间格式

​ 获取单个日历信息,考虑使用Calendar

5.Math类

Math 类提供了一序列基本数学运算和几何函数的方法

Math类是final类,并且它的所有成员变量和成员方法都是静态的。

public class Test01 {
	public static void main(String[] args) {
		
		System.out.println("求次方:" + Math.pow(3, 2));//9.0
		System.out.println("求平方根:" + Math.sqrt(9));//3.0
		System.out.println("求绝对值:" + Math.abs(-100));//100
		System.out.println("求最大值:" + Math.max(10, 20));//20
		System.out.println("求最小值:" + Math.min(10, 20));//10
		System.out.println("向下取整(地板):" + Math.floor(1.9));//1.0
		System.out.println("向上取整(天花板):" + Math.ceil(1.1));//2.0
		System.out.println("四舍五入:" + Math.round(1.5));//2
		System.out.println("获取随机值:" + Math.random());//注意:随机出0~1的数字,0包含,1排他
		
		//需求:随机出1~100的数字
		int num = (int)(Math.random()*100)+1;
		System.out.println("获取随机值:" + num);
	}
}

面试题:Math.abs会出现负数吗?

会出现负数 --> -2147483648(极限)

public class Test02 {
	public static void main(String[] args) {
		
		//获取int类型的最大值 -> 2147483647 -- 2的31次方-1
		System.out.println(Integer.MAX_VALUE);
		//获取int类型的最小值 -> -2147483648 - -2的31次方
		System.out.println(Integer.MIN_VALUE);
		
		//面试题:Math.abs会出现负数吗?
		//会出现负数 --> -2147483648
		System.out.println(Math.abs(-2147483648));
	}
}

5.1 静态导入

理解:将类中所有的静态属性和方法都导入到本类来,作为本来自己的静态属性和方法

缺点:如果类中的方法和静态导入类中的方法重名了,会调用本类自己的静态方法,所以可读性差,不建议使用

//静态导入:将类中所有的静态属性和方法都导入到本类来,作为本来自己的静态属性和方法
import static java.lang.Math.*;

public class Test03 {
	public static void main(String[] args) {
		
		System.out.println("求次方:" + pow(3, 2));//9.0
		System.out.println("求平方根:" + sqrt(9));//3.0
	}
	
	public static int sqrt(int i){
		return 123456;
	}
}

6.Random

理解:随机类

import java.util.Random;

public class Test01 {
	public static void main(String[] args) {
		
		Random ran = new Random();
		
		System.out.println("随机出int取值范围里的数值:" + ran.nextInt());
		System.out.println("随机出0~9的int数值:" + ran.nextInt(10));
		System.out.println("随机出double取值范围里的数值:" + ran.nextDouble());
		System.out.println("随机出boolean取值范围里的数值:" + ran.nextBoolean());
	}
}

6.1 深入Random

注意:seed叫做种子数,种子数一旦固定,随机出的数字就是固定的!!!

​ MyRandom myRandom = new MyRandom(种子数);

​ 为空,可随机,有了具体的数值,不可随机

import java.util.Random;

public class Test02 {
	public static void main(String[] args) {
		
		Random ran = new Random(1000);
		System.out.println("随机出int取值范围里的数值:" + ran.nextInt());
		System.out.println("随机出0~9的int数值:" + ran.nextInt(10));
		
		
		System.out.println("--------------------");
		
		MyRandom myRandom = new MyRandom();
		System.out.println("随机出int取值范围里的数值:" + myRandom.nextInt());
		System.out.println("随机出0~9的int数值:" + myRandom.nextInt(10));
	}
}

Random底层代码

public class MyRandom {
	
	//种子数
	private long seed;
	
	public MyRandom() {
		
		this(seedUniquifier() ^ System.nanoTime());
		
	}
	
	private static long seedUniquifier() {
        for (;;) {
            long current = System.currentTimeMillis();
            long next = current * 181783497276652981L;
            if (next%3==0 || next%12==0 || next%17==0){
                return next;
            }
        }
    }

	public MyRandom(long seed) {
		this.seed = seed;
	}
	
	public int nextInt(){
		return (int) seed;
	}
	
	public int nextInt(int bound){
		return Math.abs((int) seed)%bound;
	}
}

7.Runtime

理解:Runtime表示运行时系统(JVM)

Runtime代表Java程序的运行时环境,可以通过 getRuntime 方法获取当前运行时。

应用程序不能自己创建Runtime对象,可以通过Runtime的静态方法getRuntime()获得Runtime对象。

Runtime类可以访问jvm的相关信息,如处理器数量,内存信息等

public class Test01 {
	public static void main(String[] args) {
		
		//获取运行时系统对象
		Runtime run = Runtime.getRuntime();
		
		System.out.println("获取最大操作数:" + run.availableProcessors());//8
		System.out.println("获取最大内存数(byte):" + run.maxMemory());
		System.out.println("获取闲置内存数(byte):" + run.freeMemory());
		
	}
}

7.1 程序的效率(时间、内存)

public class Test02 {
	public static void main(String[] args) {
		
//		Runtime run = Runtime.getRuntime();
//		long startTime = System.currentTimeMillis();
//		long startMemory = run.freeMemory();
//		String str = "莹莹";
//		for (int i = 1; i < 50000; i++) {
//			str += "小可爱,皇冠给你带";
//		}
//		long endMemory = run.freeMemory();
//		long endTime = System.currentTimeMillis();
//		System.out.println("消耗时长:" + (endTime-startTime));//6640
//		System.out.println("消耗内存:" + (startMemory-endMemory));
		
		System.out.println("--------------------------------------------");
		
//		Runtime run = Runtime.getRuntime();
//		long startTime = System.currentTimeMillis();
//		long startMemory = run.freeMemory();
//		StringBuffer sb = new StringBuffer("莹莹");
//		for (int i = 1; i < 50000; i++) {
//			sb.append("小可爱,皇冠给你带");
//		}
//		long endMemory = run.freeMemory();
//		long endTime = System.currentTimeMillis();
//		System.out.println("消耗时长:" + (endTime-startTime));//5
//		System.out.println("消耗内存:" + (startMemory-endMemory));//2293808
		
		System.out.println("--------------------------------------------");
		
		Runtime run = Runtime.getRuntime();
		long startTime = System.currentTimeMillis();
		long startMemory = run.freeMemory();
		StringBuffer sb = new StringBuffer(450060);//优化方案:减少底层扩容次数
		sb.append("莹莹");
		for (int i = 1; i < 50000; i++) {
			sb.append("小可爱,皇冠给你带");
		}
		long endMemory = run.freeMemory();
		long endTime = System.currentTimeMillis();
		System.out.println("消耗时长:" + (endTime-startTime));//5
		System.out.println("消耗内存:" + (startMemory-endMemory));//900136
	}
}

8.System

理解:

1、System类提供了一些静态属性和方法,允许通过类名直接调用

2、System类提供了代表标准输入、标准输出、错误输出的类属性

3、System类提供了一些静态方法用于访问环境变量、系统属性的方法

8.1 System - 属性

err:“标准”错误输出流

in:“标准”输入流

out:“标准”输出流

import java.io.InputStream;
import java.io.PrintStream;
import java.util.Scanner;

public class Test01 {
	public static void main(String[] args) {
		
		//获取系统标准的输入流 - 方向:控制台->程序
		InputStream in = System.in;
		
		Scanner scan = new Scanner(in);
		System.out.println("请输入int值:");
		int num = scan.nextInt();
		
		//获取系统标准的输出流 - 方向:程序 -> 控制台
//		PrintStream out = System.out;
//		out.println(num);
		
		//获取系统标准的错误输出流 - 方向:程序 -> 控制台
		PrintStream err = System.err;
		err.println(num);
		
		scan.close();
	}
}

8.2 System.out和System.err的区别

注意:稍微感受多线程抢资源的场景

public class Test02 {
	public static void main(String[] args) {
		
		System.out.println("小明");
		System.err.println("小红");
		System.out.println("小强");
	}
}


运行结果:

小明 小明 小红 小明

小红 小强 小明 小强小红

小强 小红 小强

····

8.3 System - 方法

public static void *exit*(int status) 退出当前虚拟机

public static long *currentTimeMillis*() 获得当前系统的毫秒值(与1970 年 1 月 1 日午夜之间的时间差)–测试程序的消耗时间

public static Properties *getProperties*() 获得当前的所有系统属性

public static String *getProperty*(String key) 获得指定键的系统属性

public static void *setIn*(InputStream in) 输入重定向

public static void *setOut*(PrintStream out) 输出重定

publ****ic static void setErr****(PrintStream err) 错误重定向

import java.util.Properties;

public class Test03 {
	public static void main(String[] args) {
		
		long currentTimeMillis = System.currentTimeMillis();
		System.out.println("获取自1970年1月1日到现在的毫秒数:" + currentTimeMillis);
		
		//获取系统参数
		Properties properties = System.getProperties();
		System.out.println(properties);
		
		//通过具体Key获取对应的Value
		String property = System.getProperty("os.name");
		System.out.println("通过具体Key获取对应的Value:" + property);
		
		//拷贝数组
		int[] arr = {1,2,3,4,5,6,7,8,9,10};
		int[] newArr = new int[4];
		System.arraycopy(arr, 3, newArr, 0, 4);//(源数组,开始下标,目标数组,开始下标,拷贝长度)
		for (int num : newArr) {
			System.out.println(num);
		}
		
		//利用System.arraycopy做数组的删除功能
		String[] names = {"aaa","bbb","ccc","ddd","eee","fff",null,null,null};	
		System.arraycopy(names, 2, names, 1, 4);
		names[5] = null;
		for (String str : names) {
			System.out.println(str);
		}
		
		//退出当前虚拟机,0表示正常退出
		System.exit(0);
	}
}

9.大数值运算类

9.1 BigInteger - 整数的大数值运算类

BigInteger: 能表示比Integer更大的数字

import java.math.BigInteger;

public class Test01 {
	public static void main(String[] args) {
		
		BigInteger big1 = new BigInteger("2100000000");
		BigInteger big2 = new BigInteger("2100000000");
		
		BigInteger add = big1.add(big2);
		System.out.println("加法:" + add);
		
		BigInteger subtract = big1.subtract(big2);
		System.out.println("减法:" + subtract);
		
		BigInteger multiply = big1.multiply(big2);
		System.out.println("乘法:" + multiply);
		
		BigInteger divide = big1.divide(big2);
		System.out.println("除法:" + divide);
	}
}

9.2 BigDecimal - 小数大数值运算类

注意:

​ 1.小数做运算会失去精度,所以小数做运算都要生成大数值的运算类

​ 2.构造方法里的值使用字符串

import java.math.BigDecimal;

public class Test02 {
	public static void main(String[] args) {
		
		BigDecimal big1 = new BigDecimal("0.5");
		BigDecimal big2 = new BigDecimal("0.4");
		
		BigDecimal add = big1.add(big2);
		System.out.println("加法:" + add);
		
		BigDecimal subtract = big1.subtract(big2);
		System.out.println("减法:" + subtract);
		
		BigDecimal multiply = big1.multiply(big2);
		System.out.println("乘法:" + multiply);
		
		BigDecimal divide = big1.divide(big2);
		System.out.println("除法:" + divide);
	}
}

import java.math.BigDecimal;

public class Test03 {
	public static void main(String[] args) {
		
		BigDecimal big1 = new BigDecimal("10");
		BigDecimal big2 = new BigDecimal("3");
		
		BigDecimal divide = big1.divide(big2, 3, BigDecimal.ROUND_HALF_UP);
		System.out.println("除法:" + divide);
	}
}

简答题

1.String 转出 int 型,判断能不能转?如何转?

答:可以转,使用 Integer.parseInt(s) ,但是得处理异常 NumberFormatException。

2.Java自动装箱与拆箱

装箱就是自动将基本数据类型转换为包装器类型(int–>Integer);调用方法:Integer的valueOf(int) 方法

拆箱就是自动将包装器类型转换为基本数据类型(Integer–>int);调用方法:Integer的intValue 方法

注意:

Byte,Short,Integer,Long4种包装类默认创建了数值[-128,127]的相应类型的缓存数据;

Character创建了数值在[0,127]范围的缓存数据,Boolean直接返回True Or False,如果超出对应范围仍然会去创建新的对象。

面试题1:以下代码输出什么?

public class Main {
	public static void main(String[] args) {
		Integer i1 = 100;
		Integer i2 = 100;
		Integer i3 = 200;
		Integer i4 = 200;
		System.out.println(i1==i2);//true
		System.out.println(i3==i4);//false
	}
}

为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此

时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:

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

在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返 回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。

面试题2:以下代码输出什么?

public class Main {
	public static void main(String[] args) {
		Double i1 = 100.0;
		Double i2 = 100.0;
		Double i3 = 200.0;
		Double i4 = 200.0;
		System.out.println(i1==i2);//false
		System.out.println(i3==i4);//false
	}
}

原因: 在某个范围内的整型数值的个数是有限的,而浮点数却不是。

3.String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象。每次+操作 : 隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法拼接+后面的字符。

private final char value[];

而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,他们的底层都是可变的字符数组,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[] value,但是没有用 final 关键字修饰,所以这两种对象都是可变的。

线程安全性

String 中的对象是不可变的,也就可以理解为常量,线程安全。

AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity 、 append 、 insert 、 indexOf 等公共方法。 StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结:

​ 1.操作少量的数据: 适用 String

​ 2.单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder

​ 3.多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

4.Integer,int,String三者转换关系
Integer转int:自动拆箱,valueOf()
int转Integer:自动装箱,intValue()
String转int:Integer.parseInt
int转String:String s = i + “”;
String转Integer:valueOf(),new Integer(str)
Integer转String:toString()

5.Java 中的 Math. round(-1. 5) 等于多少?
等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。

6.String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

总结

1.包装类
注意:
1.装箱、拆箱
2.Integer的底层原理

2.字符串的类
注意:字符串类的深入

3.正则表达式 - 了解

4.日期时间类

5.Math
注意:静态导入的概念

6.Random
注意:种子数

7.System
注意:理解System.out和System.err多线程抢资源

8.Runtime

9.大数值运算类

  • 28
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A 北枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值