内部类

  • 概念:在一个类的内部再定义一个完整的类,当外部类与内部类的属性重名时,优先访问内部类属性
  • 分类:成员内部类、静态内部类、局部内部类、匿名内部类

成员内部类

  • 内部类在编译之后也会产生独立的字节码文件
  • 成员内部类在类的内部定义,与外部类的变量和方法同级别的类
  • 成员内部类可以直接拿到外部类的私有属性
  • 如果存在同名属性,优先访问成员内部类的属性
  • 成员内部类里不能定义静态成员变量,但是可以定义静态常量(final),这个静态常量在不实例化外部类的情况下可以调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Outer{
private int id = 10;
private String name = "张三";

public void out(){
System.out.println("这是外部方法");
}

public class Inner {
static final String XXX = "这是一个静态常量";
private String name = "李四";

public void in(){
System.out.println("这是内部方法");

//打印李四
System.out.println(name);
System.out.println(this.name);
//打印张三
System.out.println(Outer.this.name);
}

public void getID(){
System.out.println(id);
}
}
}

实例化成员内部类

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
// 在没有实例化外部类的情况下可调用内部类的静态常量
String xxx = Outer.Inner.XXX;
System.out.println(xxx);

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.getID();
}
}

静态内部类

  • 静态内部类不依赖外部类对象,可直接创建或通过类名访问,可以定义静态成员变量
  • 非静态内部类需要在外部类存在一个实例时才可以调用,静态内部类可以直接调用,因为没有一个外部类的实例,所以在静态内部类里面不可以直接访问外部类的属性和方法,若想访问,需要创建外部类的对象来调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Outer{

private String name = "xxx";
private int age = 20;

static class Inner{
private String address = "上海";
private String phone = "111";
private static int count = 1000;

public void show(){
Outer outer = new Outer();

System.out.println(outer.name);
System.out.println(outer.age);

System.out.println(address);
System.out.println(phone);

System.out.println(Inner.count);
}
}
}

局部内部类

  • 局部内部类就是定义在外部类的方法里面的类,作用范围和创建对象范围仅限于当前方法,不能添加任何修饰符
  • 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同(局部变量在方法执行之后消失,而内部类不会消失),变量必须修饰为final常量,这是JDK1.7的规定,JDK1.8以后,这个final会自动添加,不用我们考虑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Outer{
private String str1 = "外部类变量";
public void method(){
class Inner{
private String str = "局部变量";

public void in(){
//打印外部类变量
System.out.println(str1);

//打印局部变量
System.out.println(str);
}
}

Inner inner = new Inner();
inner.in();
}

public static void main(String[] args) {
new Outer().method();
}
}

匿名内部类

  • 匿名内部类也就是没有名字的局部内部类,正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
  • 匿名类就是在实例化类的重写方法,不使用引用保存实例
1
2
3
4
5
public class Test {
public static void main(String[] args) {
new Outer().method();
}
}

在接口上使用匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Person {
public void eat();
}

public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}

最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口

  • Thread类的匿名内部类实现
1
2
3
4
5
6
7
8
9
10
11
12
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
  • Runnable接口的匿名内部类实现
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}

Object

Object类是所有类的超类,所有类默认继承Object类

getClass()

返回引用中存储的实际对象类型

1
2
Student stu = new Student();
Class class = stu.getClass();

hashCode()

返回对象哈希值,是一个整数,表示在哈希表中的位置

哈希值:根据对象的地址或字符串或数字使用hash算法计算出来的int类型的数值,一般情况下,相同对象返回相同哈希码

1
2
Student stu = new Student();
int hash = stu.hashCode();

toString()

返回该对象的字符串表示,因为默认打印的是类的内存地址,所以通常我们都会重写这个方法,达到输出字符串的目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Student {
private String name;
private int age;

public Student(String name, int age){
this.name = name;
this.age = age
}

public String toString() {
return "name:" + name + "age:" + age;
}
}

Student stu = new Student("张三",20);
String stuInfo = stu.toString();

equals()

比较两个对象地址是否相同,这个方法在String中被重写了,重写后的方法先对比引用地址,如不相同则对比字面值

1
2
3
Student stu1 = new Student();
Student stu2 = new Student();
boolean result = stu1.equals(stu2);

finalize()

垃圾回收方法,由JVM自动调用此方法

  • 垃圾对象:没有有效引用指向此对象
  • 垃圾回收:由GC销毁垃圾对象,释放数据存储空间
  • 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象
  • 手动回收机制:使用System.gc();通知JVM执行垃圾回收

String

创建字符串的两种方式及区别

1
2
3
4
5
// 第一种创建方式,栈内引用直接指向方法区中的常量池中的值
String str1 = "你好";

// 第二种创建,堆内新建对象,对象指向方法区中的常量池中的值栈内引用指向堆内对象
String str2 = new String("Hello World");

length()

返回字符串长度

1
2
String str = "Hello World";
int leng = str.length();

charAt(int index)

返回某个位置的字符

1
2
String str = "Hello World";
char c = str.charAt(0);

contains(String str)

判断是否包含某个子字符串,返回布尔值

1
2
String str = "Hello World";
boolean result = str.contains("Hello");

toCharArray()

将字符串转换为字符数组返回

1
2
String str = "Hello World";
char[] strs = str.toCharArray();

indexOf(String str)

查找str首次出现的下标,返回,如果不存在,返回-1

1
2
3
4
String str = "Hello World";
int index = str.indexOf("Hello");
// 从第四位开始查找
int index = str.indexOf("Hello",4);

lastIndexOf(String str)

查找字符串在当前字符串中最后一次出现的下标,返回,如果不存在,返回-1;

1
2
String str = "Java Hello Java CC Java";
int index = str.lastIndexOf("Java");

trim()

去掉字符串前后空格

1
2
String str = "    Hello World    ";
String str2 = str.trim();

toUpperCase()

将小写转成大写

toLowerCase()

将大写转换成小写

1
2
3
String str = "Hello World";
String str2 = str.toUpperCase();
String str3 = str.toLowerCase();

endsWith(String str)

判断字符串是否以str结尾

startsWith(String str)

判断字符串是否以str开头

1
2
3
String str = "Hello World";
boolean r1 = str.startsWith("Hello");
boolean r2 = str.endsWith("World");

replace(char oldChar,char newChar)

将旧字符串替换成新字符串

1
2
String str = "Hello World";
String str2 = str.replace("World","Java");

split(String str)

根据str对字符串进行拆分,返回一个字符串数组

1
2
3
4
5
String str = "Hello World Java PHP C,Python|C++";
// 以空格分隔字符串
String[] strs = str.split(" ")
// 以多个符号分隔字符串空格,逗号竖线都可分隔
String[] strs = str.split("[ ,|]")

可变字符串

  • StringBuffer : 可变长字符串,运行效率慢、线程安全

  • StringBuilder : 可变长字符串、运行快、线程不安全

  • StringBuffer和StringBuilder的效率都高于String,都比String节省内存

  • StringBuffer和StringBuilder的用法是一样的,StringBuilder的效率高于StringBuffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
StringBuilder sb = new StringBuilder();
// append()追加
sb.append("Hello World");

// insert()添加
sb.insert(0,"Hello World");

// replace()替换:取前不取后
sb.replace(6,11,"Java");

// delete()删除
sb.delete(6,sb.length());

// 打印
sb.toString();

BigDecimal

  • float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,然而,它们没有提供完全精确的结果。但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("0.9");

// 加
BigDecimal result1 = bd1.add(bd2);
// 减
BigDecimal result2 = bd1.subtract(bd2);
// 乘
BigDecimal result3 = bd1.multiply(bd2);
// 除
BigDecimal result4 = bd1.divide(bd2);

// 因为除不尽会报错,所以这里保留两位小数四舍五入
BigDecimal result5 = bd1.divide(bd2).setScale(2, RoundingMode.HALF_UP)

Date

  • Date表示特定的瞬间,精确到毫秒。Date类中的大部分方法都已经被Calendar类中的方法所取代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) {
// 1 创建Date对象
Date date1 = new Date();
System.out.println(date1.toString()); //Sun Sep 19 18:53:23 CST 2021
System.out.println(date1.toLocaleString()); //2021年9月19日 下午6:53:23

// 昨天
Date date2 = new Date(date1.getTime() - (60*60*24*1000));
System.out.println(date2.toLocaleString()); //2021年9月18日 下午6:53:23

// 2 方法after before
boolean b1 = date1.after(date2);
System.out.println(b1); //true
boolean b2 = date1.before(date2);
System.out.println(b2); //false

// 比较compareTo();
int d = date1.compareTo(date1);
System.out.println(d); // 多的为1 少的为 -1

// 比较是否相等 equals()
boolean b3 = date1.equals(date2);
System.out.println(b3); // false
}

Calendar

  • Calendar提供了获取或设置各种日历字段的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
// 1. 创建 Calendar 对象
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime().toLocaleString());
// 2. 获取时间信息
// 获取年
int year = calendar.get(Calendar.YEAR);
// 获取月 从 0 - 11
int month = calendar.get(Calendar.MONTH);
// 日
int day = calendar.get(Calendar.DAY_OF_MONTH);
// 小时
int hour = calendar.get(Calendar.HOUR_OF_DAY);
// 分钟
int minute = calendar.get(Calendar.MINUTE);
// 秒
int second = calendar.get(Calendar.SECOND);
// 3. 修改时间
Calendar calendar2 = Calendar.getInstance();
calendar2.set(Calendar.DAY_OF_MONTH, x);
// 4. add修改时间
calendar2.add(Calendar.HOUR, x); // x为正就加 负就减
// 5. 补充方法
int max = calendar2.getActualMaximum(Calendar.DAY_OF_MONTH);// 月数最大天数
int min = calendar2.getActualMinimum(Calendar.DAY_OF_MONTH);
}

SimpleDateFormat

  • SimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws ParseException {
// 1. 创建对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH-mm-ss");
// 2. 创建Date
Date date = new Date();
// 格式化date(日期→字符串)
String str = sdf.format(date);
System.out.println(str);
// 解析(字符串→时间)
Date date2 = sdf.parse("1948/03/12");
System.out.println(date2);
}

System

  • 主要用于获取系统的属性数据和其他操作,构造方法私有化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
//arraycopy 复制
//src-原数组 srcPos-从哪个位置开始复制0 dest-目标数组 destPos-目标数组的位置 length-复制的长度
int[] arr = {20, 18, 39, 3};
int[] dest = new int [4];
//System.arraycopy(src, srcPos, dest, destPos, length);
System.arraycopy(arr, 0, dest, 0, 4);
for (int i : dest) {
System.out.println(i);
}

// 返回当前系统时间(毫秒)
System.currentTimeMillis();

// Arrays.copyOf(original, newLength)
}