Java进阶

更新日志:

    2021.9.3:根据狂神说的视频整理出该笔记

    2022.9.16:准备明年春招,对照一些面经等资料,做了一定修改和补充

一、常用类

1) String相关类

1.String

1.1 初识字符串

声明字符串

1
2
3
4
5
//方法一 声明时直接赋值
String s = new String("uden");
//方法二 先声明后赋值
String s = new String();
s = sc.next();

转换为二进制的方法

1
2
//插一个函数 重要
result = Integer.toBinaryString(num)//将int 数字转换为二进制字符串(String)

1.2字符串的一系列操作

连接字符串

1
2
3
4
5
6
7
8
public class join{
public static void main(String[] args){
String s1 = new String("Hello");
String s1 = new String("Java");
String s = s1+" "+s2;
System.out.println(s);
}
}

获取字符串长度

1
int size = str.length();

字符串查找

1
2
3
4
5
6
7
int index = str.indexOf(substr);//返回substr首次出现在str里的下标;
//例如
int index = str.indexOf("a");

int index = str.lastindexOf(substr);//返回substr最后一次出现在str里的下标;
//例如
int index = str.lastindexOf("a");

获取指定位置的字符

1
char mychar = str.charAt(int index);

获取子字符串

1
2
3
String substr = str.substring(int beginIndex);//获取从指定索引位置开始直到该字符串结尾的子串

String substr = str.substring(int beginIndex,int endIndex);//从begin到end的子串

去除空格

1
str.trim();

字符串替换

1
2
str.replace(char oldChar,char newChar);
str.replaceAll(String regex,String newstr);//注意这里需使用正则表达式

比较字符串
比较字符串不能用 “==” 因为字符串是对象,即使是内容相同,内存地址也是不同的,也会是false;
这里应该使用equals方法;

1
2
3
str.equals(String otherString);//返回Boolean类型

str.equalsIgnoreCase(String otherString);//忽视大小写的情况下比较字符串 返回Boolean类型

按字典顺序比较两个字符串

1
str.compareTo(String otherstr);//如果str在otherstr之前则返回一个负值,否则返回正值,如果相等则返回0;

大小写转换

1
2
3
4
5
6
7
public static void main(String[] args){
String str = new String("abc DEF");
String newstr1 = str.toUpperCase();//转换为大写
String newstr2 = str.toLowerCase();//转换为小写
System.out,println(newstr1);//注意,转换时,数字或非字符是不受影响的
System.out,println(newstr1);
}

字符串分割

1
2
3
String[] firstArray = str.split("\\.",2);
//第一个参数为正则表达式,第二个分割次数,也可以不写

字符串反转

1
2
StringBuilder stringBuilder = new StringBuilder(s);
System.out.print(stringBuilder.reverse().toString());

1.3 关于正则表达式(初级)

注意: 在正则表达式中“.”代表任何一个字符,因此在正则表达式如果想使用普通意义的"." ,需要写成"\.";
正则表达式

2. StringBuffer

2.1初始化

StringBuffer() 构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。

1
StringBuffer sb = new StringBuffer("william");

2.2 关于StringBuffer的一些方法

添加字符

1
2
3
4
5
6
7
8
//append方法  在末尾添加字符或者字符串
StringBuffer sb = new StringBuffer("william");
sb.append("wiam");//结果应是williamwiam,即添加到末尾
System.out.println(sb);

//insert方法 在指定位置添加字符
sb.insert(0,"w");
System.out.println(sb);//结果为wabcb

删除字符

1
2
3
4
5
6
7
//deleteCharAt()方法  删除指定位置的字符
sb.deleteCharAt(1);
System.out.println(sb);//结果wbcb

//delete方法 删除指定位置的字符
sb.delete(1,3);
System.out.println(sb);//结果wb 即不包括位置的最后一位

替换指定位置的字符串

1
2
3
4

//Buffer里的replace方法
sb.replace(0,1,"chengningzuishuai");
System.out.println(sb);//结果chengningzuishuaib 最后一个依然为开区间

反转字符串

1
2
3
//reverse方法
sb.reverse();
System.out.println(sb);//结果biauhsiuzgningnehc

参考:https://blog.csdn.net/weixin_44519467/article/details/103947361?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163092725916780265450059%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163092725916780265450059&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-103947361.first_rank_v2_pc_rank_v29&utm_term=stringbuffer&spm=1018.2226.3001.4187

3. StringBuilder

3.1 初始化

1
StringBuilder  sb = new StringBuilder("Hello mercury");

3.2 几个常用的方法

和StringBuffer的都一样
添加字符串

1
2
3
4
5
6
7
//append方法
sb.append("chenningzuishuai");
System.out.println(sb);//结果Hello mercurychenningzuishuai

//insert
sb.insert(2,"william");
System.out.println(sb);//结果为Hewilliamllo mercurychenningzuishuai

删除字符串

1
2
3
4
5
6
7
//deleteCharAt()方法  删除指定位置的字符
sb.deleteCharAt(1);
System.out.println(sb);//结果Hwilliamllo mercurychenningzuishuai

//delete方法 删除指定位置的字符
sb.delete(1,30);
System.out.println(sb);//结果Hshuai 即不包括位置的最后一位

替换指定位置的字符串

1
2
3
4

//Builder里的replace方法
sb.replace(0,1,"chengningzuishuai");
System.out.println(sb);```

反转字符串

1
2
3
//reverse方法
sb.reverse();
System.out.println(sb);

4. 关于这几个字符串的区别

String StringBuffer StringBuilder
String的值是不可变的(里面是final),这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间 StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 可变类,速度更快
不可变 可变 可变
线程安全 线程不安全
多线程操作字符串 单线程操作字符串

(1)如果要操作少量的数据用 String;

(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;

(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

在不考虑线程安全的情况下,现阶段应使用StringBuilder而不是StringBuffer

参考:https://blog.csdn.net/itchuxuezhe_yang/article/details/89966303?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163092981516780366546179%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163092981516780366546179&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-89966303.first_rank_v2_pc_rank_v29&utm_term=stringbuilder&spm=1018.2226.3001.4187

2) Math类

定义了一些常量,如 Math.PI Math.E

1.常用的数学方法

  1. 三角函数方法
1
2
3
4
5
6
7
public class mathdemo {
public static void main(String[] args) {
System.out.println("90度的正弦值:"+Math.sin(Math.PI/2));//注意这几个函数参数和返回值都是double
System.out.println("0度的余弦值:"+Math.cos(0));
System.out.println("60度的正弦值:"+Math.tan(Math.PI/3));
}
}
  1. 指数函数方法
1
2
3
4
5
6
7
8
9
10
11
public class ExponentFunction {
public static void main(String[] args) {//这几个函数依旧是double参数和double返回值
System.out.println("e的平方值" + Math.exp(2));//e的二次方
System.out.println("以e为底2的对数值" + Math.log(2));
System.out.println("以10为底2的对数值" + Math.log10(2));//这个10不可以变哈
System.out.println("4的平方根值" + Math.sqrt(4));
System.out.println("8的立方根值" + Math.cbrt(8));
System.out.println("2的2次方值" + Math.pow(2,2));
}
}

  1. 取整函数
1
2
3
4
//Math类里的取整函数
public static double ceil( double a);//向上取整
public static double floor( double a);//向下取整
public static double rint( double a);//返回和参数最接近的整数,如果同样接近,则结果取整数
  1. 取最大值,最小值,绝对值函数方法
1
2
3
4
5
public static void main(String[] args){
System.out.println("4和8较大者"+Math.max(4,8));
System.out.println("4.4和4较小者"+Math.min(4.4,4));
System.out.println("-7的绝对值"+Math.abs(-7));
}

3) Random类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class randomdemo {
public static void main(String[] args) {
Random r = new Random();//实例化一个random
//随机产生一个整数s
System.out.println("随机产生一个整数"+r.nextInt());
//随机产生一个大于等于0且小于10的整数
System.out.println("随机产生一个大于等于0且小于10的整数"+r.nextInt(10));
System.out.println("随机产生布尔型的值"+r.nextBoolean());
System.out.println("随机产生一个双精度型的的值"+r.nextDouble());
System.out.println("随机产生一个浮点型的的值"+r.nextFloat());

}

}

4) 大数字计算类

1. BigInteger

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BigIntegerdemo {
public static void main(String[] args) {
BigInteger big = new BigInteger("4");//实例化一个大数据
//取该数据加2的操作
System.out.println("加法操作"+ big.add(new BigInteger("2")));
System.out.println("减法操作"+ big.subtract(new BigInteger("2")));
System.out.println("乘法操作"+ big.multiply(new BigInteger("2")));
System.out.println("除法操作"+ big.divide(new BigInteger("2")));
System.out.println("乘方操作"+ big.pow(2));
System.out.println("取相反数操作"+ big.negate());
}

}

2. BigDecimal

与BigInteger不同的是,BigDecimal可以进行小数的计算,包括float和double,值得注意的是,这个的加法减法等方法需要自己去定义

1
2
3
//有两种构造方法
public BigDecimal(double val);//将double变为大数类型
public BigDecimal(String val);//将字符串变为大数类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BigDecimalDemo {

public static void main(String[] args){
BigDecimalDemo big = new BigDecimalDemo();
System.out.println("两数相加的结果"+big.add(7.5,8.9));
}


public BigDecimal add(double value1, double value2){//加法
BigDecimal b1 = new BigDecimal(Double.toString(value1));//这里直接填value1也可以
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.add(b2);
}
}

二、集合

集合的索引也是从0开始

1)Collection接口

Collection是根接口,一般不直接使用,后面的list接口和Set接口都继承了Collection接口,因此是通用的;而遍历集合一般都是通过迭代器实现;这里给出一个实例

1
2
3
4
5
6
7
8
9
10
11
12
import java.util*
public class Muster{
public static void main(){
Collection<String> list = new ArrayList<>();
list.add("a");
Iterator<String> it = list.iterator();//迭代器是集合的一种方法
while(it.hasNext()){//这个判断一定要记得,不然会抛异常
String str = it.next();
System.out.println(str);
}
}
}

除此之外,collection中主要有以下方法

方法 功能描述
add(E e) 添加指定对象
remove(Object o) 将指定对象移除
isEmpty 判断是否为空
itertor() 迭代器
size() 获取元素个数

2)List集合

List中主要包含了两种实现类,即ArrayList和LinkedList,他们的主要区别就是,List中类似数组存储,而LinkedList则是使用链表存储。

1
2
3
//实例化
List<E> list = new ArrayList<>();
List<E> list2 = new LinkedList<>();

List集合继承了collection接口,有他的所有方法,并且多了两个方法:

方法 作用
get(int index) 获取指定那个位置的集合元素
set(int index,Object obj) 顾名思义就是设置元素的

来个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ArrayListdemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.remove(2);//移除指定位置的集合元素
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}

3)Set集合

Set继承了Collection接口;Set集合对象不按特定的顺序排序,只是简单的加入数据,但是Set集合中不能包含重复的对象,常用的实现类有HashSet类和TreeSet类

1. HashSet实现类

数据结构:JDK1.8之前:哈希表(数组+单向链表);JDK1.8之后:哈希表(数组+单向链表+红黑树),当链表长度超过阈值(8)时,链表将转换为红黑树。

特点:查询快,元素无序,元素不可重复,没有索引;

底层分析:哈希表底层用数组+单向链表实现,即使用链表处理冲突,同一Hash值的元素都存储在一个链表里,但是当位于一个链表中的元素较多,即Hash值相等的元素较多,通过key值依次查找的效率降低。JDK1.8之后,哈希表底层采用数据+单向链表+红黑树实现,当链表长度超过阈值(8)时,链表将转换为红黑树,极大缩短查询时间。

2. TreeSet实现类

数据结构:红黑树

特点:查询快,元素有序,元素不可重复,没有索引;

底层分析:TreeSet实现了继承于Set接口的SortedSet接口 ,它支持两种排序方法,自然排序和定制排序,自然排序的意思就是放入元素“a”,“b”,a会自然地排在b前面,其中还有几个特有方法。

first() 返回第一个元素; last() 返回最后一个元素;comparator() 返回排序比较器;

引用自https://blog.csdn.net/weixin_42559574/article/details/108203595?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163110337616780255269862%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163110337616780255269862&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-108203595.first_rank_v2_pc_rank_v29&utm_term=Java集合类&spm=1018.2226.3001.4187

3)Map集合

Map没有继承collection类,它提供的是key到value的映射,Map中不能包含相同的key,每个key只能映射一个value;其实key就相当于下标。

1. Map接口

其包含的主要方法

方法 功能
put(K key,V value) 添加指定的key到value的映射关系
containKey(Object key) 如果包含指定key的映射关系,则返回true
containsValue(Object value) 如果包含指定key的映射关系,则返回true
get(Object key) 如果存在指定的key对象,则返回对应的value值,否则返回null
keySet() 返回所有的key的值形成的Set集合
values() 返回所有的value的值形成的Collection集合

2. Map接口实现的类

主要有HashMap和TreeMap,建议使用HashMap,因为效率更高,而TreeMap有一定的顺序,若希望Map集合里的对象也有一定的顺序,则使用TreeMap实现Map集合

1
2
3
4
//实例化
//HashMap
Map<String,String> map = new HashMap<>();
Map<String,String> map = new TreeMap<>();

三、 枚举类型

1)枚举类的使用

  1. 枚举类的理解:类的对象只有有限个,确定的,就为枚举类
  2. 当需要定义一组常量时,强烈建议使用枚举类
  3. 如果枚举类中只有一个对象,则可以作为单例模式的实现方式

2)使用enum类型来定义常量

1
2
3
4
5
public enum con{
con_A,
con_B,
con_C
}

3)枚举类的常用方法

方法名称 作用 使用方法
values() 将枚举类型的成员以数组的形式返回(也可以通过该方法获取枚举类类型的成员) 枚举类型名称.values()
valuesOf() 将普通字符串转换为枚举实例 枚举类型名称.valueOf(“abc”)
compareTo() 比较两个枚举对象在定义时的顺序(正值表示在参数在调用该方法的枚举对象之前,负值则是之后) 枚举对象.compareTo()
ordinal 得到枚举成员的位置索引 枚举对象.ordinal()

构造函数在写枚举类构造方法的时候权限修饰符应该为private;

四、泛型

1)初识泛型

把元素的类型设计成一个参数,这个参数就是泛型

如果集合不使用泛型

就会出现种种问题

使用泛型

问题迎刃而解

然后输出(这里复习一下增强for循环和迭代器)
问题迎刃而解

然后输出(这里复习一下增强for循环和迭代器)

注意点:集合中<>中填泛型时不能时基本数据类型(比如int),必须是一个类,例如是int需要填包装类Integer

2)自定义泛型

1、泛型类

这里的T相当于一个参数,就当成其他的数据类型一样用

1
2
3
4
5
6
7
8
9
10
11
12
13
public class test{
@Test
public void test1(){
//如果定义了泛型类,实例化没有指明类的泛型,则认为泛型类型为Object类型
//要求:如果大家定义了类是带泛型的建议在实例化要指明类的泛型
Order order = new Order();
order.setOrderT(123);
order.setOrderT("ABC");
//建议:实例化时指明类的泛型
Order<String> order1 = new Order<String>("orderAA")//这里有一个构造方法传参,String就是传入类的泛型参数
}

}
  • 如果子类在继承带泛型的父类的时候,指明了泛型的类型,则在实例化子类的对象时,不再需要指明泛型

  • 静态方法中不能使用泛型

  • 异常类不能是泛型类

3)泛型方法

泛型方法里的泛型参数和泛型类里的泛型参数是没有关系的

上图中因为返回的时List类型的所以还有个List,特别注意标明是泛型方法要有个 < E >
测试泛型方法

泛型方法是可以声明为静态的.原因就是:泛型参数实在调用方法时确定的,并非是在实例化类时确定的

4)通配符的使用

通配符 : ?

1
2
3
4
5
6
7
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while(iterator.hasNext()){
Object obj = iterator.next;//这里当然不能用 ? obj 声明啦
System.out.println(obj);
}
}

有限制条件的通配符的使用

  • ? extends Person 向下限制,只接受Person类以下的类型

  • ? super Person 向下限制,只接受Person类以上的类型

1
2
3
4
5
//实例
public void doSomething(A<? extends List> a){
}
//实例
List<? extends list> list = null;


比如在这里 list1 = list5 就会报错

五、注解

1)初识注解


2)元注解

元注解主要是在定义自定义的注解的时候用

3)自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//自定义注解
public class Test03{
//注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation2(age = 18,name = "陈帅");
public void test(){}
@Target({Element.TYPE,Elementtype.NETHOD})
@Retention(RetenionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数: 参数类型 + 参数名();
String name() default "";//这里设置默认值为空,这样就不用传参了
int age();
int id() default -1 ;//设置默认值为-1
String[] schools default("西柚牛逼","Mercury最帅");
}
@Target({Element.TYPE,Elementtype.NETHOD})
@Retention(RetenionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}
}

如果有default即有默认值,可以不用赋值

六、反射

1)关于静态语言和动态语言

2)初识反射


例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//什么反射
public class Test02{
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的Test的对象
Class c1 = class.forName("com.kuang.reflection.User");//User在这个位置
System.out.println(c1);

Class c2 = class.forName("com.kuang.reflection.User");
Class c3 = class.forName("com.kuang.reflection.User");
Class c4 = class.forName("com.kuang.reflection.User");

//一个类在内存中只有一个class类
//一个类被加载后,类的整个结构都会被封装在class对象中
System.out.println(c2.hashcode());
System.out.println(c3.hashcode());
System.out.println(c4.hashcode());
}
}

class User{...}//在这里就不详细列出来了

3)获取class的实例

实操获取

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 Test{
public static void main(String[] args) throws ClassNotFoundExcepion{
Person person = new Student();
System.out.println("这个人是" + person.name);

//方式一:通过对象获取
Class c1 = person.getClass();
System.out.println(c1.hashcode());

//方式二:forname获取
Class c2 = Class.forname("com.reflection.Student")
System.out.println(c2.hashcode());

//方式三:通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashcode());

}

}

class Person{...}
class student extends Person{...}

4)关于类加载器

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//获得类的信息
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
class c1 = Class.forname("com.kuang.reflection.User");
User user = new user();
c1 = user.getclass();

//获取类的名字
System.out.println(c1.getName);//获取类名 + 包名
System.out.println(c1.getSimpleName());//获取类名

//获得类的属性
Field[] fields = c1.getFields();//只能找到public属性的

fields = c1.getDeclaredFields();
for(Field field : fields){
System.out.println(field);

}

//获得类的方法
Method[] methods = c1.getMethods();
for(Method method : methods){
System.out.println("正常的"+method);
}
methods = c1.getDeclaredMethods();
for(Method method : method){
System,out.println("getDeclaredMethods"+method);
}

//获取指定方法
Method getName = c1.getMethod("getName",null);//获取getName这个方法 getname方法没有参数
Method setName = c1.getMethod("setName",String.class);
//这里要丢一下参数,编译器才知道你要什么方法
//这里是调用setname的String重载,所以要丢一个String.class进去
System.out.println(getName);
System.out.println(setName);
}

//获得指定构造器
Constructor[] constructors = c1.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructors);
}
}

//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
System.out.println(declaredConstructor);

5)通过反射创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test{
public static void main(String[] args) throws ClassNotFoundException,IllegalAccessException{
//获取class对象
Class c1 = Class.forname("com.reflection,User");

//1.普通创建一个对象
User user = (user)c1.newInstance(); //本质是调用了类的无参构造
System.out.println(user);

//2.获得指定的构造器创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
User user2 = (user)constructor.newInstance("陈帅",001,18); //有参构造
System.out.println(user2);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName",String.class);

//invoke:激活
//(对象,“方法的值”)
setName.invoke(user3,"陈帅");
System.out.println(user3.getName());

//通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");

//不要直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true);
name.setAccessible(true);
name.set(user4,"陈帅牛逼");
System.out.println(user4.getName());


实操

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class xingneng {
//普通方法调用
public static void test() {
user user = new user();
long starTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endtime = System.currentTimeMillis();
System.out.println("普通方法执行10次"+(endtime-starTime)+ "ms");
}

//反射方法调用
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
user user = new user();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);

long starTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}

long endtime = System.currentTimeMillis();
System.out.println("反射方法执行10次"+(endtime-starTime)+ "ms");
}

//反射方法调用 关闭检测
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
user user = new user();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long starTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}

long endtime = System.currentTimeMillis();
System.out.println("反射方法执行10次"+(endtime-starTime)+ "ms");
}

public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
test();
test2();
test3();
}
}

程序运行结果

所以关闭检测可以提升程序的效率

6)反射操作泛型


7)反射操作注解

getAnnotations

getAnnotation

七、多线程

1、实现多线程

多线程在我目前开发中用的不算多,这里知识简单的梳理一下多线程的知识点。
实现 多线程的方法在Java中有两种,一种是继承thread类,还有一种方法就是重写runable方法

1)、继承thread类

这种方法就是写一个类来继承thread类,在这个类中还需要写一个run方法来运行这个线程,然后在主类中就可以创建这个线程然后启动该线程。以代码为例

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
28
29
30
31
32
33

class Thread1 extends Thread{
//创建一个类继承thread类
private String name;
public Thread1(String name) {
this.name=name;
}
public void run() {
//需要在该线程里面执行的方法
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}

//主类
public class Main {

public static void main(String[] args) {
Thread1 mTh1=new Thread1("A");//创建一个线程的对象
Thread1 mTh2=new Thread1("B");
mTh1.start();//启动该线程
mTh2.start();

}

}

在线程运行的时候,需要注意下:两个线程启动时,在处理机上是并发运行的,因此线程输出的顺序并不确定。(所以在做PTA上面的题可能多提交几遍说不定就可以了。)

2) 重写Runable接口

具体需要子类实现Runable接口,然后重写里面的run方法就可以了。

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
28
29
30
31
class Thread2 implements Runnable{
//继承
private String name;

public Thread2(String name) {
this.name=name;
}

@Override
public void run() {//重写run方法
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

}

public class Main {
public static void main(String[] args) {
new Thread(new Thread2("C")).start();//在主类中创建这个线程并且启动
new Thread(new Thread2("D")).start();
}

}

线程有好几种状态,如: 阻塞状态,准备状态等等,这一部分的内容以及关于线程的调度问题具体请参照操作系统部分

2、多线程常用的函数

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
28
start() 函数

//主要应用于在主类中启动该线程。(需要先new 一个多线程的对象。

sleep()函数

//sleep(long millils) 在指定的毫秒数内让线程休眠

join()函数

//等待该线程的终止

yield()函数

//暂停当前正在执行的线程对象,让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会

setPriority()函数

//更改线程的优先级

interrupt()函数

向线程发出一个中断信号,让线程在无限等待的时候能够抛出,从而结束线程(并不是中断哪个线程)

wait()函数

//Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,
//与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。

3、线程同步

1)、synchronized关键字

  • synchronized关键字的作用域有二种:
  1. 是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
  2. 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
  • 除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象;

  • synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;

总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

异常

自动装箱机制

深拷贝 浅拷贝 引用拷贝

I/O


Java进阶
http://example.com/2021/09/03/Java进阶/
作者
Mercury
发布于
2021年9月3日
许可协议