Java笔记(二)


枚举(Enumeration)和注解(Annotation)

[toc]

一、枚举

引出枚举类:

需求:创建Season类,包含季节的值,且季节的值是有限的4个(春夏秋冬),此类只读,不需要修改。

枚举类

自定义枚举类

  • 规范

    • 不提供set()方法,枚举类通常只读;
    • 对枚举对象/属性使用final static共同修饰,实现底层优化;
    • 枚举对象名通常使用全部大写,常量的命名规范;
    • 枚举对象根据需要可以有多个属性。
  • 特点

    • 构造器私有化;
    • 本类内部创建一组对象;
    • 对外暴露对象(通过为对象添加public final static修饰符)
    • 可以提供get()方法,但不提供set()方法。
  • 代码演示

public class test1 {
	public static void main(String[] args) {
		//访问对象实例
		System.out.println(Season.AUTUMN);
		System.out.println(Season.SUMMER);
	}
}

class Season
{
	private String name;
	private String desc;
	//定义四个对象
	public final static Season SPRING = new Season("春天","温暖");
	public final static Season SUMMER = new Season("夏天","炎热");
	public final static Season AUTUMN = new Season("秋天","凉爽");
	public final static Season WINTER = new Season("冬天","寒冷");
	//1.将构造器私有化,防止直接new处对象
	//2.去掉set()方法,防止属性被修改
	//3.在Season内部直接创建固定(数量)的对象
	//4.可以加final修饰符优化
	private Season(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public String getName()
	{
		return name;
	}
	public String getDesc()
	{
		return desc;
	}

	@Override
	public String toString() {
		return "Season{" +
				"name='" + name + '\'' +
				", desc='" + desc + '\'' +
				'}';
	}
}

/*
输出:
	Season{name='秋天', desc='凉爽'}
	Season{name='夏天', desc='炎热'}
 */

enum枚举类

  • 代码演示
public class test1 {
	public static void main(String[] args) {
		//访问对象实例
		System.out.println(Season.AUTUMN);
		System.out.println(Season.SUMMER);
	}
}
//使用enum关键字实现枚举类
enum Season
{
	//1.使用enum替代class
	//2.public final static Season SPRING = new Season("春天","温暖");直接用
	//  SPRING("春天","温暖")代替,常量名(实参列表)
	//3.若有多个常量(对象),则用逗号间隔即可
	//4.若使用enum实现枚举类,要求将定义常量对象写在最前面
    //5.若使用无参构造器创建对象,则可以省略括号和实参列表
	SPRING("春天","温暖"),SUMMER("夏天","炎热"),AUTUMN("秋天","凉爽"),WINTER("冬天","寒冷");
	private String name;
	private String desc;
	private Season(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public String getName()
	{
		return name;
	}
	public String getDesc()
	{
		return desc;
	}
	@Override
	public String toString() {
		return "Season{" +
				"name='" + name + '\'' +
				", desc='" + desc + '\'' +
				'}';
	}
}

/*
输出:
	Season{name='秋天', desc='凉爽'}
	Season{name='夏天', desc='炎热'}
 */
  • 注意事项

    • 当使用enum关键字实现枚举类时,默认继承Enum类,而且是一个final类;
    • public final static Season SPRING = new Season("春天","温暖");SPRING("春天","温暖")代替,根据其对应参数(的个数)确定使用的构造器;
    • 若使用无参构造器创建对象,则可以省略括号和实参列表;
    • 当有多个枚举对象时,使用 ,间隔,最后用;结尾;
    • 枚举对象必须放在枚举类的首行。
  • enum实现接口

    • 使用enum关键字后,就不能再继承其他类了,因为enum会隐式继承Enum类,而Java是单继承机制;
    • 枚举类和普通来一样可以实现接口,形式:enum 类名 implements 接口1,接口2{}

Enum成员方法

  1. toString()Enum类默认的toString()直接返回对象名,但可以重写;
  2. name():返回对象名称;
  3. value():返回Season[],遍历取出定义的所有对象;
  4. valueOf():将字符串转换成枚举对象,要求字符串必须为已有变量名,否则报异常;
  5. compareTo():比较两个枚举常量,比较的是编号(前面对象的编号-后面对象的编号)。

代码演示:(Season类见上节)

public class test1 {
	public static void main(String[] args) {
		Season autumn = Season.AUTUMN;
		//1.name():返回对象名称
		System.out.println(autumn.name()); //AUTUMN
		//2.ordinal():返回声明次序/序号(从0开始)
		System.out.println(autumn.ordinal()); //2
		//3.value():返回Season[],遍历取出定义的所有对象
		Season[] values = Season.values();
		for(Season season : values) //增强for循环
		{
			System.out.println(season);
		}
		/*
		Season{name='春天', desc='温暖'}
		Season{name='夏天', desc='炎热'}
		Season{name='秋天', desc='凉爽'}
		Season{name='冬天', desc='寒冷'}
		 */
		//4.valueOf():将字符串转换成枚举对象,要求字符串必须为已有变量名,否则报异常
		Season autumn1 = Season.valueOf("AUTUMN");
		System.out.println("autumn1="+autumn1);//autumn1=Season{name='秋天', desc='凉爽'}
		System.out.println(autumn == autumn1);//true
		//5.compareTo():比较两个枚举常量,比较的是编号(前面对象的编号-后面对象的编号)
		System.out.println(Season.SUMMER.compareTo(Season.AUTUMN)); //-1 (1-2)
		System.out.println(Season.AUTUMN.compareTo(Season.SUMMER)); //1 (2-1)
	}
}

二、注解

  • 注解:也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、 构造器、局部变量等数据信息。

  • 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。

  • JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据 了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。

基本的注解介绍:

使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素。

➢三个基本的Annotation:

  • @Override:限定某个方法,是重写父类方法,该注解只能用于方法;
  • @Deprecated:用于表示某个程序元素(类,方法等)已过时;
  • @SuppressWarnings:抑制编译器警告。

Override

  1. @Override只能修饰方法,不能修饰属性、类、包等;
  2. 如果写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,若重写,则编译通过,否则不通过;
  3. @interface表示一个注解类。

Deprecated

  1. @Deprecated修饰某个元素,表示元素已过时;
  2. 即使不推荐使用,但人可以使用;
  3. 可以修饰方法、类、字段、包、参数、等;
  4. @Deprecated可以做版本升级过渡使用。

SuppressWarnings

  1. 编写代码时,如果不希望看到警告,可以用@SuppressWarnings
  2. @SuppressWarnings({"all"}),在{""}中写入不想看到的警告,all表示全部警告不显示;
  3. @SuppressWarnings的作用范围与放置位置有关;

元注解*

  1. JDK的元Annotation用于修饰其他Annotation;
  2. 元注解的种类(使用不多,了解,不用深入研究)
    • Retention //指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
    • Target //指定注解可以在哪些地方使用
    • Documented //指定该注解是否会在javadoc体现
    • Inherited //子类会继承父类注解

异常(Exception)

一、基本介绍

  1. 基本概念
  • Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
  1. 执行过程中所发生的异常事件可分为两大类

    • Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory),Error 是严重错误,程序会崩溃。

    • Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]。

二、异常体系图

  1. 异常体系图

    • 异常体系图体现了继承和实现的关系;
    • 虚线代表实现接口,实线代表继承。

  2. 小结

    • 异常分为两大类,运行时异常和编译时异常;
    • 运行时异常,编译器不要求强制处置的异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常,对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响;
    • 编译时异常,是编译器要求必须处置的异常。

三、五大运行时异常

  1. NullPointerException:空指针异常

    • 当应用程序试图在需要对象的地方使用null时,抛出该异常,如下:

    String name = null; System.out.println(name.length());

  2. ArithmeticException:数学运算异常

    • 当出现异常的运算条件时,抛出此异常,如除0等。
  3. ArraylndexOutOfBoundsException:数组下标越界异常

    • 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
  4. ClassCastException:类型转换异常

    • 当试图将对象强制转换为不是实例的子类时,抛出该异常。

    • 代码示例:

public class test1 {
	public static void main(String[] args) {
		A b = new B();//向上转型,可以运行
        B b2 = (B)b; //向下转型,可以运行
        C b2 = (C)b; //抛出类型转换异常
    }
}
class A{}
class B extend A{}
class C extend A{} 
  1. NumberFormatException:数字格式不正确异常

    • 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常==>使用该异常我们可以确保输入是满足条件数字。

    • 代码示例:

public class test1 {
	public static void main(String[] args) {
		String str1 = "1234";
        String str2 = "抛出异常!";
        int num1 = Integer.parseInt(name1); 
        System.out.println(num1);//正常运行输出int 类型的1234
        int num2 = Integer.parseInt(name2); 
        System.out.println(num2);//抛出异常
    }
}

四、编译异常

编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。

  1. 常见的编译异常
    • SQLException:操作数据库时,查询表可能发生异常;
    • IOException:操作文件时,发生的异常;
    • FileNotFoundException:当操作一个不存在的文件时,发生异常;
    • ClassNotFoundException:加载类,而该类不存在时,异常;
    • EOFException:操作文件,到文件末尾,发生异常;
    • IllegalArguementException:参数异常。

五、异常处理

try-catch方式

  1. try-catch-finally:程序员在异常中捕获发生的异常,自行处理。

    • 处理机制:
try {
    //代码(可能有异常)
}catch(Exception e)
{
    //捕获到异常
    //1.当异常发生时,系统将异常封装成Exception对象e,传递给catch
    //2.得到异常对象后,程序员自己处理
}finally //(可以不写)
{
    //不论代码块有无异常,始终要执行finally中的代码
    //通常将释放资源的代码放在此
}
  1. 使用细节
    • 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块;
    • 如果异常没有发生,则顺序执行try的代码块,不会进入到catch
    • 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码finally
    • 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointerException在前),如果发生异常,只会匹配一个catch
    • 可以进行try-fianll配合使用,这种用法相当于没有捕获异常因此程序会直接崩溃/退出。应用场景:执行一段代码,无论是否发生异常,都必须执行某个业务逻辑。
public class test1 {
	public static void main(String[] args) {
		try {
			Person person = new Person();
			person = null;
			System.out.println(person.getName()); //NullPointerException
			int a = 1,b=0;
			System.out.println(a/b); //ArithmeticException
		} catch (NullPointerException nullPointerException) { //子类catch写在父类catch前面
			System.out.println("空指针异常:"+nullPointerException.getMessage());
		}catch (ArithmeticException arithmeticException) //子类catch写在父类catch前面
		{
			System.out.println("算术异常:"+arithmeticException.getMessage());
		}catch (Exception e)
		{
			System.out.println("异常:"+e.getMessage());
		}
	}
}
class Person
{
	String name = "Jack";
	public String getName() {
		return name;
	}
}

throws方式

  1. throws:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM。

    • 处理机制:(如果没有任何处理代码,则默认throws)

      image-20220405161355511

  2. 使用案例

    • throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类,如Exception
    • throws关键字后也可以是异常列表,即可以抛出多个异制。
public void f2() throws FileNotFoundException,NullPointerException {
		//创建了一个文件流对象
		//1.这里的异常是一个FiLeNotFoundException编译异常
		//2.可以使用try-catch-finally
		//3.也可以使用throws,抛出异常,让调用f2方法的调用者(方法)处理
		//4.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类,如Exception
		//5.throws关键字后也可以是异常列表,即可以抛出多个异制
		FileInputStream fis = new FileInputStream("d://aa.txt");
	}
  1. 使用细节
    • 对于编译异常,程序中必须处理,比如try-catch 或者throws
    • 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理,出现异常的地方将异常抛给调用它的类或对象,类或对象再向上抛,直至抛给JVM虚拟机,JVM虚拟机接受的异常后输出异常、结束程序;
    • 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型;
    • throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws
class Father
{
	public void method() throws RuntimeException {}
}
class Son extends Father
{
	@Override
	public void method() throws ArithmeticException { //ArithmeticException为RuntimeException的子类
	}
	//或者
//	public void method() throws RuntimeException {
//	}
}

六、自定义异常

  1. 一般情况下,用运行异常RuntimeException,好处是我们可以使用默认的处理机制。
public class test1 {
	public static void main(String[] args) {
		int age = 180;
		if(!(age >= 18 && age <= 120))
		{
			throw new AgeException("输入的年龄有误!");
		}
		System.out.println("输入的年龄正确!");
	}
}
//自定义异常
class AgeException extends RuntimeException
{
	public AgeException(String message) {
		super(message);
	}
}

七、throw和throws的区别

意义 位置 后面跟的
throws 异常处理的一种方式 方法声明处 异常类型
throw 手动生成异常对象的关键字 方法体中 异常对象

常用类

一、包装类

  1. 包装类(Wrapper)的分类

    • 针对八种基本数据类型相应的引用类型-包装类;
    • 有了类的特点,就可以调用类中的方法。
    基本数据类型 包装类
    boolean Boolean
    char Character
    byte Byte
    short Short
    int Interger
    long Long
    float Float
    double Double
  2. 装箱和拆箱

    Integer <--> int为例:

    • jdk5前的手动装箱和拆箱方式,装箱:基本类型->包装类型,反之,拆箱;
    • jdk5以后(含jdk5)的自动装箱和拆箱方式;
    • 自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
    • 其他类型的一样。
public class test1 {
	public static void main(String[] args) {
		//演示 Integer <--> int
		//手动装箱:int --> Integer
		int age = 180;
		Integer integer1 = Integer.valueOf(age);
		//手动拆箱:Integer --> int
		int i = integer1.intValue();

		//自动装箱:int --> Integer
		int a = 100;
		Integer integer2 = a; //底层任然使用 Integer.valueOf()
		//手动拆箱:Integer --> int
		int j = integer2; //底层仍然使用intValue()
	}
}
  1. 包装类型和String类型的相互转换

    IntegerString为例:

public class test1 {
	public static void main(String[] args) {
		//演示 包装类(Integer)--> String
		Integer i = 100;//自动装箱
		//方式1
		String str1 = i + ""; //此时str为String类型,原来的i没变
		//方式2
		String str2 = i.toString();
		//方式3
		String str3 = String.valueOf(i);

		//String --> 包装类(Integer)
		String str4 = "1234";
		//方式1
		Integer j = Integer.parseInt(str4); //使用到自动装箱
		//方式2
		Integer integer = Integer.valueOf(str4);
	}
}
  1. 常用的包装类方法
Integer integer = Integer.valueOf(str4);
System.out.println(Integer.MIN_VALUE);//返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值
System.out.println(Character.isDigit('a'));//判断是不是数字
System.out.println(Character.isLetter('a'));//判断是不是字母
System.out.println(Character.isUpperCase('a'));//判断是不是大写
System.out.println(Character.isLowerCase('a'));///判断是不是小写
System.out.println(Character.isWhitespace('a'));//判断是不是空格
System.out.println(Character.toUpperCase('a'));//转成大写
System.out.println(Character.toLowerCase('A'));//转成小写
  1. Integer创建机制

    • 经典面试题1
      public class test1 {
      	public static void main(String[] args) {
      		Integer i = new Integer(1);
      		Integer j = new Integer(1);
      		System.out.println(i == j);//False (两个新对象,用 == 比较为False)
      
      		//所以,这里主要是看范围-128~127就是直接返回
      		Integer m = 1;//底层Integer.valueOf(1);->阅读源码,valueOf(n)根据n的范围确定返回的对象是否为新创建的
      		Integer n = 1;//底层 Integer.valueOf(1);
      		System.out.println(m == n);//True (返回2个1,同一个对象)
      
      		//所以,这里主要是看范围-128~127就是直接返回,否则,就new Integer(xx);返回新对象
      		Integer x = 128;//底层Integer.valueOf(1);
      		Integer y = 128;//底层Integer.valueOf(1);
      		System.out.println(x == y);//False (返回两个新创建的对象)
      		/*
      		valueOf()底层代码:
      		当传入 i >= -128 && i <= 127 时直接从数组返回,否则就 new Integer(i)
      		public static Integer valueOf(int i) {
      			if (i >= Integer.IntegerCache.low && i <= Integer.IntegerCache.high)
      				return Integer.IntegerCache.cache[i + (-Integer.IntegerCache.low)];
      				//cache[]数组在类加载的时候就创建好了,缓存了-128~127之间的数,用的时候调用
      			return new Integer(i);
      		}
      		 */
              Integer a = new Integer(127); //新建对象
      		Integer b = 127; //cache数组中取出
      		System.out.println(a == b);//False
      
      		//只要有基本数据类型,只判断值是否相等
      		Integer c = new Integer(127);
      		int d = 127;
      		System.out.println(c == d); //True
      	}
      }

      二、String类

String类结构剖析

  1. String类的理解

    • String对象用于保存字符串,也就是一组字符序列;
    • 字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、”boy“等;
    • 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
    • String类较常用构造器(其它看手册):
      • String s1=new String();
      • String s2=new String(String original);
      • String s3=new String(char[] a);
      • String s4=new String(chartl a,int startlndex,int count);
    • String是final类,不可被继承;
    • String有属性private final char value[],用于存放字符串内容,valuefinal修饰的,不可修改(指不可修改地址指向,但单个字符内容可以改变);
  2. String结构图

    image-20220405161411709

  3. 两种创建String对象的方法

    • 方式一:直接赋值String s="hello";
      • 先从常量池查看是否有”hello”数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址;
    • 方式二:调用构造器 String s2=new String("hello");
      • 先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有”hello”,重新创建,如果有,直接通过value指向。最终指向的是中的空间地址。

String对象特性

  1. 字符串的特性
    • String是一个final类,代表不可变的字符序列;

    • 字符串是不可变的,一个字符对象一旦被分配,其内容是不可变的。

    • string s="a";创建了一个字符串s+="b",实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。

      =>结论:如果我们对字符串做大量修改,不要使用String。

  2. 相关例题
    • String s1 = "hell0"; s1 = "haha";共创建了2个对象(s1重新指向常量池中的”haha”,不会销毁”hello”)。
    • String a = "hello"+"abc";共创建了1个对象,编译器底层会优化,自动拼接再创建对象,相当于String a = "helloabc";
    • 如下:
public class test1 {
	public static void main(String[] args) {
		String a = "123";
		String b = "456";
		//分析:根据源码可得
		//1.先创建一个对象 StingBuilder sb = new StringBuild();
		//2.执行 sb.append("123");
		//3.执行 sb.append("456");
		//4.String ab = sb.toString();
		//最后ab指向堆中的对象(String)value[] -> 常量池中 "123456"
		String ab = a + b;
		System.out.println(ab);
	}
}
  1. 重要规则
    • String c1 = "ab"+"cd";常量相加,看常量池
    • String c = a + b;变量相加,看

String类的常见方法

  1. format()
public class test1 {
	public static void main(String[] args) {
		String name = "Jack";
		int age = 20;
		double score = 95.555;
		char gender = '男';
		// %s %d %.2f %c 都为占位符
        // %s:表示字符串占位
		// %d:表示整数占位
		// %.2f:表示小数占位,保留2位小数(四舍五入)
		// %c:表示字符占位
		String formatString = "姓名:%s,年龄:%d,成绩:%.2f;性别:%c";
		String info1 = String.format(formatString,name,age,score,gender);
		String info2 = String.format("姓名:%s,年龄:%d,成绩:%.2f;性别:%c",name,age,score,gender);
		System.out.println(info1);//姓名:Jack,年龄:20,成绩:95.56;性别:男
		System.out.println(info2);//姓名:Jack,年龄:20,成绩:95.56;性别:男
	}
}
  1. 其它见Java API

三、StringBuffer类

基本介绍

  1. java.lang.StringBuffer代表可变的字符序列,可以对字符串序列进行增删;

  2. 很多方法与String相同,但StringBuffer是可变长度的;

  3. StringBuffer是一个容器。

  4. StringBuffer的直接父类是AbstractstringBuilder

  5. StringBuffer实现了Serializable,即StringBuffer的对象可以串行化(可以进行网络传输,可以保存到文件);

  6. 字符序列存放在在父类中AbstractstringBuilder属性 char[] value,不是final;该value数组存放字符串内容,因此存放在中;

  7. StringBuffer是一个final类,不能被继承。

StringBuffer对象的创建

//1.创建一个大小为 16 的char[],用于存放字符内容
StringBuffer stringBuffer1 = new StringBuffer();
//2.创建一个大小为 100 的char[],用于存放字符内容
StringBuffer stringBuffer2 = new StringBuffer(100);
//3.通过给定一个String来创建StringBuffer,char[]的大小就是str.length()
StringBuffer stringBuffer3 = new StringBuffer("hello");

String VS. StringBuffer

  1. String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低原因:private final char []value
  2. StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高;
  3. 因为StringBuffer字符内容是存在char[]value,有扩容机制,所以在变化(增加/删除)时不用每次都更换地址(即不是每次创建新对象),所以效率高于String(char []value放在堆中);
  4. String-->value[]-->常量池StringBuffer-->value[]-->堆

String和StringBuffer相互转换

//1.String --> StringBuffer
String str = "hello";
//方式1:使用构造器
//*注意:堆str没有影响,返回的stringBuffer1才是StringBuffer对象
StringBuffer stringBuffer1 = new StringBuffer(str);
//方式2:使用append()方法
StringBuffer stringBuffer2 = new StringBuffer();
stringBuffer2 = stringBuffer2.append(str);

//1.StringBuffer --> String
//方式1:使用StringBuffer提供的toString()方法
StringBuffer stringBuffer3 = new StringBuffer("hello");
String str1 = stringBuffer3.toString();
//方式2:使用构造器
String str2 = new String(stringBuffer3);

StringBuffer常用方法

  • 增:append()
  • 删:delete(start,end)
  • 改:replace(start,end,string);//将strat~end间的内容替换掉,不含end
  • 查:indexOf() //查找子串在字符串第一次出现的索引,若找不到返回-1
  • 插:insert
  • 获取长度:length()

踩坑

public class test1 {
	public static void main(String[] args) {
		String str = null;
		StringBuffer stringBuffer1 = new StringBuffer();
		stringBuffer1.append(str);//根据源码,底层调用的是 AbstractStringBuilder 的 appendNull
		//此时stringBuffer1的内容为 "null"
		System.out.println(stringBuffer1.length());//4
		System.out.println(stringBuffer1); //null

		//super(str.length() + 16); str = null 时,会出现空指针异常
		StringBuffer stringBuffer2 = new StringBuffer(str);//NullPointerException
	}
}

四、StringBuilder类

基本介绍

  1. 一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全)。该类被设计用作 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快;
  2. 在StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。
  3. StringBuilder的直接父类是AbstractstringBuilder
  4. StringBuilder实现了Serializable,即StringBuiler的对象可以串行化(可以进行网络传输,可以保存到文件);
  5. StringBuilder对象字符序列仍然是放在其父类AbstractstringBuilderchar []value中,因此字符序列在堆中;
  6. StringBuilder的方法,没有做互斥的处理(即无synchronization修饰),因此在单线程下使用。

常用方法

  • StringBuffer一样。

五、String、StringBuffer、StringBuilder

三者的比较

  1. StringBuffer和StringBuilder非常类似,均可代表字符序列,而且方法也一样;

  2. 执行效率:StringBuilder > StringBuffer > String

  3. String不可变字符序列,效率低,但复用性高;

  4. StringBuffer可变字符序列,效率高(增删),线程安全;

  5. StringBuilder可变字符序列,效率最高,线程不安全;

  6. 如果要对字符串进行大量修改,不要使用String,使用StringBuffer(多线程)StringBuilder(单线程)

三者的使用场景

  1. 如果字符串存在大量的修改操作,一般使用StringBufferStringBuilder
  2. 如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
  3. 如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer;
  4. 如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等。

六、Math类

Math类主要使用其方法。

Math类中的方法都为静态(static),可直接通过Math.方法名()调用。

  1. 常用方法
    • abs():绝对值
    • pow():求幂
    • ceil():向上取整
    • floor():向下取整
    • round():四舍五入
    • sqrt():求开方
    • random():求随机数(返回0~1之间的一个随机小数)
      • x=(int)(2+Math.random()*6)返回整数x(2<= x <= 7)
      • x=Math.random()*6返回小数x(0<= x < 6)
      • x=2+Math.random()*6返回小数x(2<= x < 8)
    • max():求两数最大值
    • min():求两数最小值

七、Arrays类

常用方法

Arrays包含了一系列静态方法,用于管理和操作数组(如排序和搜索)。

  1. toString():返回数组的字符串形式;

  2. sort():排序自然排序和定制排序;

  3. binarySearch():通过二分法查找,要求排好序,如int index=Arrays.binarySearch(arr,3);

  4. copyOf():数组元素的复制(Integer[] newArr=Arrays.copyOf(arr,arr.length););

  5. fill():数组元素的填充(Integer[] num=new Integer[]{9,3,2};);

  6. equals():比较两数组元素内容是否一致(boolean equals=Arrays.equals(arr,arr2););

  7. asList():将一组值转换成List(List<Integer> asList=Arrays.asList(2,3,4,5,6,1););

sort排序的解读

  1. 自然排序和定制排序
public class test1 {
	public static void main(String[] args) {
		//使用sort方法排序
		Integer arr[] = {1,-1,7,0,55};
		//1. 数组是引用类型,所以通过sort排序后会直接影响到实参 arr
		//2. sort重载的,也可以通过传入一个接口 Comparator 实现定制排序
		//3. 调用定制排序时,传入了2个参数:排序的数组arr和实现了Comparator接口的匿名内部类
		// 	 要求我们实现的compare()方法,由compare()方法返回值决定排序方式
		//默认排序
		Arrays.sort(arr);
		System.out.println("排序后:"+Arrays.toString(arr)); //排序后:[-1, 0, 1, 7, 55]
		//定制排序
		Arrays.sort(arr, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
//				return o1 - o1; //从小到大
				return o2 - o1; //从大到小
			}
		});
		System.out.println("定制排序后"+Arrays.toString(arr));//定制排序后[55, 7, 1, 0, -1]
	}
}
  1. 定制排序实现冒泡排序
import java.util.Arrays;
import java.util.Comparator;

public class test1 {
	public static void main(String[] args) {
		int []arr = {1,6,2,5,9,29,7,45};
		bubble_1(arr);
		System.out.println("普通冒泡排序:"+Arrays.toString(arr));
		//普通冒泡排序:[1, 2, 5, 6, 7, 9, 29, 45]

		bubble_2(arr, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return o2-o1; //从大到小
//				return o1-o2; //从小到大
			}
		});
		System.out.println("定制的冒泡排序:"+Arrays.toString(arr));
		//定制的冒泡排序:[45, 29, 9, 7, 6, 5, 2, 1]
	}
	//普通冒泡排序
	public static void bubble_1(int []arr)
	{
		int tmp = 0;
		for (int i = 0; i < arr.length - 1; i++) {
			for(int j = 0;j < arr.length - 1 - i;j++)
			{
				if(arr[j] > arr[j+1]) //从小到大
			 // if(arr[j] < arr[j+1]) //从大到小
				{
					tmp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = tmp;
				}
			}
		}
	}
	//定制的冒泡排序
	public static void bubble_2(int []arr,Comparator comparator)
	{
		int tmp ;
		for (int i = 0; i < arr.length - 1; i++) {
			for(int j = 0;j < arr.length - 1 - i;j++)
			{
				//compare()返回值的大小决定排序方式
				if(comparator.compare(arr[j],arr[j+1]) > 0)
				{
					tmp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = tmp;
				}
			}
		}
	}
}

八、System类

常用方法

  1. exit:退出当前程序;
  2. arraycopy:复制数组元素,比较适合底层调用;
  3. currentTimeMillens:返回当前时间距离1970-1-1的毫秒数;
  4. gc:运行垃圾回收机制System.gc()

九、大数处理:BigInteger和BigDecimal

BigerIntege

  1. 创建大数:BigInteger bigInteger = new BigInteger("999999999999999999999999999999");(字符串形式);
  2. 四则运算:不能直接用+-*/,需要用对应的方法操作:
    • add():加;subtract():减;multiply():乘;divide():除
import java.math.BigInteger;
public class test1 {
	public static void main(String[] args) {
		//大数处理
		BigInteger bigInteger1 = new BigInteger("999999999999999999999999999999");
		BigInteger bigInteger2 = new BigInteger("100");
		System.out.println("原:"+bigInteger1);
		//大数运算
		System.out.println("加:"+bigInteger1.add(bigInteger2));
		System.out.println("减:"+bigInteger1.subtract(bigInteger2));
		System.out.println("乘:"+bigInteger1.multiply(bigInteger2));
		System.out.println("除:"+bigInteger1.divide(bigInteger2));
	}
}

/*
输出:
	原:999999999999999999999999999999
	加:1000000000000000000000000000099
	减:999999999999999999999999999899
	乘:99999999999999999999999999999900
	除:9999999999999999999999999999
 */

BigDecimal

  1. 当我们需要保留一个精度很高的数时,double不够用,可以用BigDecimal;

  2. 创建精度高的小数:BigDecimal bigDecimal = new BigDecimal("199.99999999999999999999999999");(字符串形式);

  3. 四则运算方法同BigerIntege,但进行除法运算时,若出现结果是无限小数时,会出现ArithmeticException异常,解决方法:在除法运算时,用BigDecimal.ROUND_CEILING指定精度即可,如下:

    System.out.println("除:"+bigDecimal1.divide(bigDecimal2,BigDecimal.ROUND_CEILING));//若出现无限小数,则结果的精度为分子的精度

十、日期类

第一代日期类(Date)

  1. Date:精确到毫秒,代表特定的瞬间;
  2. SimpleDateFormat:格式和解析日期的类,SimpleDateFormat 格式化和解析日期的具体类。它允许进行格式化(日期->文本)、解析(文本->日期)和规范化。
  3. 代码演示:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class test1 {
	public static void main(String[] args) throws ParseException {//转换时抛出异常
		//1. 获取当前时间,默认格式是国外的,可根据需求转换,此处Data是java.util中的
		Date date = new Date();
		System.out.println("当前时间(默认格式):"+date);
		//输出:当前时间(默认格式):Tue Apr 05 11:04:23 CST 2022
		//2. 获取某个时间对应的毫秒数
		System.out.println(date.getTime());
		//输出:1649128077124
		//3. 通过指定的毫秒数来获取时间
		Date date1 = new Date(36000);
		System.out.println(date1);
		//输出:Thu Jan 01 08:00:36 CST 1970

		//4. 格式转换
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
		String format = simpleDateFormat.format(date);//format将日期转换成指定格式
		System.out.println("格式转换后的时间:"+format);
		//输出:格式转换后的时间:2022年04月05日 11:15:51 周二
		//把一个字符串转换成对应的Date
		String str = "1996年01月25日 10:20:31 星期四";
		Date date2 = simpleDateFormat.parse(str);//转换若字符串格式和指定格式不同时会抛出异常
		System.out.println("字符串转日期(默认格式):"+date2);
		//输出:字符串转日期(默认格式):Thu Jan 25 10:20:31 CST 1996
		System.out.println("字符串转日期(修正格式):"+simpleDateFormat.format(date2));
		//输出:字符串转日期(修正格式):1996年01月25日 10:20:31 周四
	}
}

第二代日期类(Calendar)

  1. Calendar是一个抽象类,并且构造器是protected类,需要用getInstance()获取实例,如:Calender c = Calender.getInstance();

  2. 直接输出Calender实例会输出一个含有一系列字段的字符串,可根据需求选取;

  3. Calender没有专门的格式化方法,可根据需求组合输出。

import java.util.Calendar;
public class test1 {
	public static void main(String[] args) {
		//创建日历实例
		Calendar c = Calendar.getInstance();
		System.out.println(c);
		//输出:java.util.GregorianCalendar[time=1649129565436,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=3,WEEK_OF_YEAR=15,WEEK_OF_MONTH=2,DAY_OF_MONTH=5,DAY_OF_YEAR=95,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=11,HOUR_OF_DAY=11,MINUTE=32,SECOND=45,MILLISECOND=436,ZONE_OFFSET=28800000,DST_OFFSET=0]
		//获取某个字段
		System.out.println("年:"+c.get(Calendar.YEAR));//年:2022
		//月要 +1,因为Calender的月从0开始
		System.out.println("月:"+(c.get(Calendar.MONTH)+1));//月:4
		System.out.println("日:"+c.get(Calendar.DAY_OF_MONTH));//日:5
		System.out.println("12进制小时:"+c.get(Calendar.HOUR));//12进制小时:11
		System.out.println("24进制小时:"+c.get(Calendar.HOUR_OF_DAY));//24进制小时:11
		System.out.println("分钟:"+c.get(Calendar.MINUTE));//分钟:32
		System.out.println("秒:"+c.get(Calendar.SECOND));//秒:45
		//Calender没有专门的格式化方法,可根据需求组合输出
		System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"+c.get(Calendar.DAY_OF_MONTH)+"日");
		//输出:2022年4月5日
	}
}

第三代日期(LocalDateTime)

  1. 前面两代日期类的不足分析:

    JDK1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK1.1引入Calendar类之后被弃用了。而Calendar也存在问题是:

    • 可变性:像日期和时间这样的类应该是不可变的;
    • 偏移性:Date中的年份是从1900开始的,而月份都从0开始;
    • 格式化:格式化只对Date有用,Calendar则不行;
    • 此外,它们也不是线程安全的;不能处理闰秒等(每隔2天,多出1s);
  2. 声明方式

    • LocalDateTime ldt=LocalDateTime.now();//获取年月日时分秒
    • LocalDateTime ldt = LocalDate.now();//只能获取年月日
    • LocalDateTime ldt = LocalTime.now();//只能获取时分秒
  3. DateTimeFormatter格式化:类似于SimpleDateFormat:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("格式");

    String str= dtf.format(日期对象);

  4. Instant时间戳:类似于Date,提供了一系列DateTimeFormatterDate相互转换的方式:

    • Instant –> Date:Date date = Date.from(instant);
    • Date –> Instant:Instant instant = date.toInstant();
  5. LocalDateTime提供了plusminus方法可以对当前时间进行加或者减;

    • plusDay()、plusMonths()、plusHours()等等;
    • minusDay()、minusMonths()、minusHours()等等;
  6. 代码演示

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class test1 {
	public static void main(String[] args) {
		//1. LocalDateTime演示
		LocalDateTime ldt=LocalDateTime.now();//获取年月日时分秒
		//LocalDateTime ldt = LocalDate.now();//只能获取年月日
		//LocalDateTime ldt = LocalTime.now();//只能获取时分秒
		System.out.println(ldt);
		//输出:2022-04-05T12:47:15.763112100
		System.out.println("年="+ldt.getYear());//年=2022
		System.out.println("月="+ldt.getMonth());//月=APRIL
		System.out.println("月="+ldt.getMonthValue());//月=4
		System.out.println("日="+ldt.getDayOfMonth());//日=5
		System.out.println("时="+ldt.getHour());//时=12
		System.out.println("分="+ldt.getMinute());//分=47
		System.out.println("秒="+ldt.getSecond());//秒=15

		//2. plus 和 minus 演示
		//当前时间 +800 天后的时间
		LocalDateTime localDateTime = ldt.plusDays(800);
		System.out.println("800天后的时间:"+localDateTime);
		//输出:800天后的时间:2024-06-13T13:16:22.456071300
		//当前时间 -3 月后的时间
		LocalDateTime localDateTime2 = ldt.minusMonths(3);
		System.out.println("3个月前的时间:"+localDateTime2);
		//输出:3个月前的时间:2022-01-05T13:17:28.344965

		//使用DateTimeFormatter进行格式化
		//类似于SimpleDateFormat
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss E");
		String str= dtf.format(ldt);
		System.out.println(str);
		//输出:2022年04月05日 01:03:06 周二
	}
}

文章作者: 一袖南烟顾
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 一袖南烟顾 !
评论
  目录