java常用类

常用类

一、内部类

1.1 基本概念

在类的内部定义的类,叫做内部类。

分为:

  • 成员内部类

  • 静态内部类

  • 局部内部类

  • 匿名内部类

public class Outter {
    // 在类的内部定义的类
    class Inner{
        
    }
}

在类的内部定义类,一般还是封装思想的体现,可以将该类定义为私有类。

在一个类中,可以定义四种情况的变量,对应四种内部类,使用方式也相似

public class Outter {
    String s1; // 成员变量(实例变量)、属性
    
    static String s2; // 静态属性,类属性
    
    public void m1() {
        String s3 = new String("aaa"); // 局部变量
        
        System.out.println(new String("hello")); // 匿名变量
    }
}

1.2 成员内部类【了解】

成员内部类是直接在类的内部定义的普通类。

注意:

  • 不能定义静态属性

  • 不能定义静态方法

  • 在内部类的方法中,如果要调用外部类的属性,可以直接使用,但是如果内部类有同名属性或局部变量,可以使用外部类的名称.this.属性名。

  • 在外部类的静态方法中,不能直接创建成员内部类的对象

由于成员内部类的功能有缺陷(不能定义静态属性,不能定义静态方法),一般很少使用。

public class Outter {
    String name; // 成员变量(实例变量)、属性
    
    // 成员内部类
    class Inner{
        String name;
        // 不能定义静态属性
//      public static String s;
        
        public void m1() {
            System.out.println(Outter.this.name);
            System.out.println(name);
        }
        
        // 不能定义静态方法
//      public static void m2() {}
    }
    
    public void m3() {
        Inner inner = new Inner();
    }
    
    public static void m2() {
//      Inner inner = new Inner(); // 在外部类的静态方法中,不能直接创建成员内部类的对象
    }
}
​
public class Demo1 {
    public static void main(String[] args) {
        Outter outter = new Outter();
        outter.name = "aaa";
//      System.out.println(outter.name);
        
        Outter.Inner inner = outter.new Inner();
        inner.name = "bbb";
        inner.m1();
        
        // 必须先创建外部类的对象才能创建内部类的对象
        Outter.Inner inner1 = new Outter().new Inner();
    }
}

1.3 静态内部类【重点】

静态内部类是在类的内部定义的静态类。使用方式与普通类一样,一般在项目中使用静态内部类来体现封装的特征。

public class Outter1 {
    public static String name;
    
    public static class Inner1{
        public String a; // 可以定义属性
        public static String name; // 可以定义静态属性
        
        public void m1() { // 可以定义普通方法
            
        }
        
        public static void m2() {// 可以定义静态方法
            
        }
    }
    
    public void m1() {
        new Inner1(); // 可以在普通方法中创建对象
    }
    
    public static void m2() {
        new Inner1();// 可以在静态方法中创建对象
    }
}
​
public class Demo2 {
    public static void main(String[] args) {
        Outter1.Inner1 inner = new Outter1.Inner1();
        inner.a = "aaa";
    }
}

1.4 局部内部类【了解】

在类的方法中定义的类称为局部内部类。

  • 不能定义静态成员和静态方法

  • 不能在类前面添加private、public等关键字

  • 必须在方法中定义后立即使用,否则出了作用域范围会无效

public class Outter2 {
    public void m1() {
        
        class Inner{
            // 不能定义静态成员和静态方法
            private String name = "aaa";
            public void m2() {
                System.out.println(name);
            }
        }
        
        Inner in = new Inner();
        in.m2();
    }
}

1.5 匿名内部类【重点】

当需要将一个抽象类或者接口创建对象时,会要求继承该抽象类或者实现接口才可以创建对象。如果需要直接对该抽象类或接口创建对象,并且只使用一次,可以使用匿名内部类的方式。

public interface MyInterface {
    void m1();
}
​
public abstract class MyClass {
//  public abstract void m1();
}
​
public class Person {
    public void use(MyInterface m) {
        m.m1();
    }
}
​
public class Demo1 {
    public static void main(String[] args) {
        // 创建一个接口对象的同时实现,匿名内部类
        MyInterface m = new MyInterface() {
            @Override
            public void m1() {
                System.out.println("m1");
            }
        };
        
        m.m1();
        
        // 使用抽象类创建匿名内部类
//      MyClass m1 = new MyClass() {
//          @Override
//          public void m1() {
//              System.out.println("m1===");
//          }
//      };
//      
//      m1.m1();
        
        // 如果接口或抽象类没有抽象方法,匿名内部类相对奇怪
        MyClass m1 = new MyClass() {};
        
        // 如果方法要传入一个接口或抽象类的对象,可以使用匿名内部类
        Person p = new Person();
        p.use(new MyInterface() {
            @Override
            public void m1() {
                System.out.println("m1");
            }
        });
        
        // jdk1.8中的lambda表达式
//      p.use(() -> System.out.println("m1"));
    }
}

二、Object类

Object类是所有类的父类,无论是否直接继承。

2.1 getClass()

得到对象的实际类型,一般用来比较两个对象是否同一个类的对象。

也可以通过类名.class来得到类型。

public class Demo1 {
    public static void main(String[] args) {
        Animal a = new Dog();
        Dog d = new Dog();
        if(a instanceof Dog) {
            System.out.println("a对象是Dog类的对象");
        }
        
        // 判断对象是否某个类型
        if(a.getClass() == Dog.class) {
            System.out.println("a对象是Dog类的对象");
        }
        
        // 判断是否同一个类型的对象
        if(a.getClass() == d.getClass()) {
            System.out.println("a对象和d对象是同一类型");
        }
        
        // 得到类名
        System.out.println(a.getClass().getName());
        // 得到短类名
        System.out.println(a.getClass().getSimpleName());
    }
}

2.2 hashCode()

返回对象的十进制的hash码值,使用哈希算法根据对象的地址或者字符串或数字计算出来的int型的值。

自定义类的对象hashCode默认返回对象的地址。

一般情况下需要重写hashCode方法,以便更好的利用hash算法。

注意:hashCode并不唯一,必须保证相同的对象具备相同的hashCode,尽量保证不同对象有不同的hashCode。

public class Student {
    private String id;
    private String name;
    private int age;
    
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }
}

2.3 equals方法

用来比较两个对象是否相同。

默认是比较地址,在项目中如果需要比较两个对象是否相同,应该重写equals方法。

public class Student {
	private String id;
	private String name;
	private int age;
	

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
        // 如果地址相同
		if (this == obj)
			return true;
        // 如果比较对象为空
		if (obj == null)
			return false;
        // 如果不是同一个类型
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
        // 如果id属性为空
		if (id == null) {
			if (other.id != null)
				return false;
            // 如果id属性不相同
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
}

注意:equals方法和hashCode方法应该一起重写。

2.4 toString()方法

当将对象作为字符串使用或输出时,会自动调用toString方法,而该方法默认输出hashCode值(地址),没有意义,应该重写toString以便输出对象信息。

public class Student {
	private String id;
	private String name;
	private int age;
	
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}
public class Demo3 {
	public static void main(String[] args) {
		Student s1 = new Student("1001", "你开心", 18);
		System.out.println(s1);
		String s = s1 + "====";
		System.out.println(s);
	}
}

2.5 finalize()方法

当对象被判定为垃圾对象时,由JVM自动调用,将该对象标识为垃圾,并进入回收队列。

垃圾对象:没有有效引用的指向的对象,即为垃圾对象。

垃圾回收:释放内存空间。

自动回收:JVM会定时进行垃圾回收,如果内存空间占满,会强制进行垃圾回收。

手动回收:使用System.gc()方法,通知JVM进行垃圾回收。

2.6 Java中的垃圾回收机制

在JVM中,内存分为以下区域:程序计数器、栈、方法区、堆。

在项目中,使用new关键字创建的内容一般都在堆中,堆的大小理论来说可以与内存大小挂钩。在项目中程序计数器、栈、方法区一般不需要管理,只有堆空间需要进行内存管理,所以垃圾回收就是指堆空间的内存管理。

堆的分代

  • Java的堆是JVM中最大的一块内存区域,主要保存Java中各种类的实例。为了更好的管理堆中各个对象的内存,包括分配内存和回收内存。

  • JVM将堆分成了几块区域:

    • 新生代(Young) 新生代占堆的1/3空间

      • 新生代又分为: Eden、 Survivor1、 Survivor2

      • 新生代中的Eden\Survivor1\Survivor2空间占比为 8:1:1

    • 老年代(Old) 老年代占堆的2/3空间

垃圾回收涉及到的算法:标记、复制、整理、计数、分代

三、包装类

3.1 基本使用

是指基本数据类型所对应的引用数据类型。

int对应Integer、char对应Character,其他基本数据类型都是首字母大写变包装类,例如:Float、Byte

所有的包装类都是final的,不能被继承。

public class Demo1 {
	// 基本数据类型对应包装类
	public static void main(String[] args) {
		int n = 5;
		// 将基本数据类型转换成包装类
		Integer in = new Integer(n);// 1、使用new
		Integer in1 = n; // 2、直接赋值,jdk1.5的新特性,自动装箱
		Integer in2 = Integer.valueOf(n); // 3、使用valueOf
		
		double d = 2.0;
		Double d1 = new Double(d);// 1、使用new
		Double d2 = d; // 2、直接赋值,jdk1.5的新特性,自动装箱
		Double d3 = Double.valueOf(d); // 3、使用valueOf
		
		// 将包装类转换成基本数据类型
		int n1 = in; // 直接赋值,jdk1.5的新特性,自动拆箱
		int n2 = in.intValue(); // 使用intValue
		
		// 基本数据类型的默认值是前面数组中使用过的默认值,例如:int默认值为0
		// 但是包装类由于都是Object类的子类,所以默认值为null
		Integer in3 = null;
		// 包装类和基本数据类型在大多数时候都可以互换使用
		Integer in4 = 1;
		int n3 = new Integer(3);
		
		// 将字符串转换成数字
		int n4 = Integer.parseInt("23");
		double d4 = Double.parseDouble("25.5");
		// 转换失败会出现异常java.lang.NumberFormatException
		int n5 = Integer.parseInt("23a");
	}
}

3.2 整数缓冲区(常量池)

  • 在加载类的时候,会在方法区创建256个Integer对象,范围是-128~127之间。如果使用valueOf或者直接赋值int会在该缓冲区中拿到对象直接使用,超出范围才会new一个新的Integer。

public static Integer valueOf(int i) {
    // 如果范围是-128~127之间
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        // 返回常量池中(数组)的对象
        return IntegerCache.cache[i + (-IntegerCache.low)];
    // 否则返回一个new的对象
    return new Integer(i);
}

优点:

  • 效率高,使用速度快,不用new

  • 不用创建过多的重复对象,节约空间

四、String类

Java中使用的字符串值都是此类的对象。

创建方式有两种:

public static void main(String[] args) {
    // 创建方式有两种
    String s1 = "hello";
    String s2 = new String("hello");
}

原理:

  • 是一个final类,不能被继承。

  • 存储字符串的方式是采用字符数组。

  • 该数组是final的,表示不能改变该数组的地址。

  • String的值是不可变的

  • String也是使用常量池的,如果直接赋值,是在常量池中创建对象,如果使用new,是在堆中创建对象。

// 部分源码
public final class String // final类
    implements java.io.Serializable, Comparable<String>, CharSequence {
    // final的字符数组
    private final char value[];
}
public class Demo3 {
	public static void main(String[] args) {
		// 创建方式有两种
		String s1 = "hello";
		String s2 = new String("hello");
		
		String s3 = "hello";
		String s4 = new String("hello");
		
		System.out.println(s3==s4); // false
	}
}

常见的字符串类中的方法:

public class Demo4 {
	public static void main(String[] args) {
		String s = "hello, world! hello, world";
		// 根据下标获取字符
		char ch = s.charAt(0);
		System.out.println(ch);
		// 判定一个字符串是否包含另一个字符串
		boolean b = s.contains("llo");
		System.out.println(b);
		// 将字符串转换成字符数组
		char[] array = s.toCharArray();
		System.out.println(Arrays.toString(array));
		// 查找一个字符串在另一个字符串中首次出现的位置,得到相应的下标,如果不存在返回-1
		int index = s.indexOf("world");
		System.out.println(index);
		// 查找一个字符串在另一个字符串中出现的位置,从最后开始查找,得到相应的下标,如果不存在返回-1
		int index1 = s.lastIndexOf("world");
		System.out.println(index1);
		// 返回字符串的长度
		int length = s.length();
		System.out.println(length);
		// 去掉字符串前后的空格
		String s1 = "        hello, world    ";
		s1 = s1.trim(); // 字符串的值是不可变的,所有改变字符串的结果的方法一定有返回值,应该接收返回值
		System.out.println(s1);
		// 将小写字母转成大写
		s = s.toUpperCase();
		System.out.println(s);
		// 将大写字母转成小写
		s = s.toLowerCase();
		System.out.println(s);
		// 判定一个字符串是否以另一个字符串结尾
		boolean b1 = s.endsWith("rld");
		System.out.println(b1);
		// 判定一个字符串是否以另一个字符串开头
		boolean b2 = s.startsWith("rld");
		System.out.println(b2);
		// 将一个字符串中的包含的字符串全部替换成其他字符串
		s = s.replace("hello", "a");
		System.out.println(s);
		// 从指定位置开始截取部分字符串,截取后面所有的
		System.out.println(s.substring(4));
		// 从指定位置开始截取部分字符串,截取到另一个位置,从4到8
		System.out.println(s.substring(4, 8));
		// 将字符串根据指定的内容进行切割,成一个数组
		String[] strings = s.split("!");
		System.out.println(Arrays.toString(strings));
        // 将两个字符串进行拼接
		String s2 = "hello";
		String s3 = "world";
		s2 = s2.concat(s3);
		System.out.println(s2);
	}
}

在使用字符串时,可能会发生编译器优化,例如下面的变量s3和s4:

public class Demo5 {
	public static void main(String[] args) {
		String a = "a";
		String b = "b";
		String ab = "ab";
		String s1 = a + b; // 使用StringBuilder创建出来的结果,相当于new一个对象
		System.out.println(s1 == ab); // false
		String s2 = a + "b"; // 使用StringBuilder创建出来的结果,相当于new一个对象
		System.out.println(s2 == ab); // false
		// 两个常量,结果绝对不会发生其他的变化,就是ab,所以编译器会进行优化,直接将代码优化为String s3 = "ab";
		String s3 = "a" + "b"; 
		System.out.println(s3 == ab); // true
		// 两个常量,结果绝对不会发生其他的变化,就是ab,所以编译器会进行优化,直接将代码优化为String s4 = "ab";
		final String a1 = "a";
		String s4 = a1 + "b";
		System.out.println(s4 == ab); // true
		String s5 = a.concat(b); // 拼接字符串,结果是new的
		System.out.println(s5 == ab); // false
		String s6 = ab.concat(""); // 当拼接的内容为null或者长度为0时,直接返回原字符串
		System.out.println(s6 == ab); // true
		String s7 = s2.intern(); // 直接得到常量池中的地址,相当于String s7 = "ab";
		System.out.println(s7 == ab); // true
	}
}

五、可变字符串

当需要频繁改变字符串时,应该使用可变字符串。

StringBuffer:JDK1.0提供,安全性高,性能较低。

StringBuilder:JDK1.5提供,性能较高,不安全。

基本用法:

public class Demo6 {
	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer();
		// 往最后追加内容
		sb.append("hello");
		sb.append("world");
		// 修改
		sb.replace(3, 5, "aaaaaaa");
		// 插入内容
		sb.insert(1, "bbb");
		// 删除内容
		sb.delete(1, 4);
		System.out.println(sb);
	}
}

原理:

  • 使用无参构造时,默认创建的字符数组大小为16个char

  • 可以在创建时指定大小。

  • 如果指定字符串,会设置大小为字符串的长度加上16

  • 当空间不够时,需要扩容,扩容的大小是原本的大小的2倍+2

// 部分源码
// 默认大小为16
public StringBuffer() {
    super(16);
}

// 指定大小
public StringBuffer(int capacity) {
    super(capacity);
}

// 如果指定字符串,会设置大小为字符串的长度加上16
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

void expandCapacity(int minimumCapacity) {
    // 当空间不够时,需要扩容,扩容的大小是原本的大小的2倍+2
    int newCapacity = value.length * 2 + 2;
    if (newCapacity - minimumCapacity < 0)
        newCapacity = minimumCapacity;
    if (newCapacity < 0) {
        if (minimumCapacity < 0) // overflow
            throw new OutOfMemoryError();
        newCapacity = Integer.MAX_VALUE;
    }
    value = Arrays.copyOf(value, newCapacity);
}

六、BigDecimal

注意:使用BigDecimal计算使用应该使用字符串作为保存数字的参数。

作用:

  • 小数的精确计算

  • 超出long范围的整数计算

public class Demo7 {
	public static void main(String[] args) {
		// 加法
		BigDecimal b3 = new BigDecimal("3435");
		BigDecimal b4 = new BigDecimal("656757");
		System.out.println(b3.add(b4));
		
		// 减法
		BigDecimal b1 = new BigDecimal("1.0");
		BigDecimal b2 = new BigDecimal("0.9");
		System.out.println(b1.subtract(b2));
		
		// 乘法
		BigDecimal b5 = new BigDecimal("3456567457567856756756");
		BigDecimal b6 = new BigDecimal("4353453453453453453453");
		System.out.println(b5.multiply(b6));
		
		// 除法
		BigDecimal b7 = new BigDecimal("5");
		BigDecimal b8 = new BigDecimal("2");
		// 除法如果可以除尽,那么可以只用一个参数
		System.out.println(b7.divide(b8));
		
		BigDecimal b9 = new BigDecimal("5");
		BigDecimal b10 = new BigDecimal("3");
		// 除法如果不能除尽,那么需要指定保留的小数位,以及保留小数的方式,例如:四舍五入
		System.out.println(b9.divide(b10, 5000, RoundingMode.HALF_UP));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值