javaEE

java高级

一、常用API

1、Object类

1、作用:
	(1)它是java中任何引用类的父类。包括我们自己定义的类、和java类库中的类。
	(2)任何的引用类都从Object类中继承了一些方法。例如:toString()、equals()方法。
2、使用:
	(1)它是java.lang包下的。
3、方法:
	toString():打印所有的属性的值。(我们的类需要重写此方法)
	equals():比较“两个对象”的所有属性值是否完全相同。

2、Objects类

1、作用:
	此类内部定义了大量的对"对象"操作的一些"静态方法。
2、使用:
	(1)它是java.util包下的。
3、方法:
	equals:此方法可以比较两个对象“是否完全相同”。(此方法也是基于我们对象的equals()方法进行判断的	

4、Object的equals和Objects的equals方法的比较。
	(1)Object的equals方法。如果对象不一样。会抛出空指针异常
	(2)Objects的equals方法。不会抛出空指针异常。
	(3)对于“空引用”,Objects的equals()方法更安全。

3、Date类(日期类)

1、作用:
	它代表了一个特定的时间,精度:毫秒。
2、使用:
	(1)它是java.util包下的。
	(2)构造方法:
		Date():使用当前系统时间构造一个Date对象。
		Date(long m):使用一个“毫秒值”构造一个Date对象。
3、方法:	
	getTime():获取毫秒值。
	setTime(long m):设置毫秒值。

4、DateFormat类(日期格式)

1、作用:
	DateFormat(抽象类)它可以对Date进行“格式化显示”的一个工具类。
2、使用:
	(1)它在java.text包下
	(2)此类是一个“抽象类”,不能直接创建对象,可以使用它的子类。
	(3)java.text.SimpleDateFormat(子类)
3、构造方法:
		SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss);
4、方法:
	parse():将String转换为Date格式(此格式必须跟字符串中的日期格式“匹配”否则抛异常)。
	format():将Date转换为String。

5、Calendar类(日历类)

1、作用:
	Calendar(抽象类):它代表一个“日历”,可以进行日期运算。使用他的子类GregorianCalendar

2、使用:
	(1)它是java.util包下的。
	(2)使用格式:(简单的方式获取这个子类对象)
		Calendar c = Calendar.getInstance();
3、方法:
	get(): 获取某字段值。例如:int year = c.get(Calendar.YEAR);(月需要加一处理)。
	set():设置某字段值。例如:把c设置为1996年    c.set(Calendar.YEAR,1996)。
	add():将某字段增加指定的值。例如:设置100天后  c.add(Calendar.DAY_OF_MONTH,100);

6、System类(系统类)

1、作用:
	它包含了一些实用的“静态方法”。
2、使用:
	(1)它是java.lang包下的。

3、方法:
	arraycopy:复制数组。
		格式:arraycopy(原数组,原数组的起始位置,目标数组,目标数组的索引位置,复制的数量)
	currentTimeMillis:获取当前系统时间的毫秒值。
	exit:结束JVM。(也就是结束程序)。
		格式:System.exit(0);

7、StringBuilder字符串拼接

1、作用:
	(1)由于String的不可变性,导致做“字符串连接”时,效率低下。Java为我们提供了一种类:StringBuilder专门做“字符串连接”的。
	(2)区别:
		String拼接字符串会在内存中创建新的空间。
		StringBuilder不会创建新的空间,直接向原来存储字符串空间里面进行添加。

2、使用:
	(1)它是java.lang包下的。

3、构造方法:
	StringBuilder():构造一个有16个空位置的StringBuilder对象。
	StringBuilder("Hello")

4、方法:
	append() :拼接各种类型的数据。
	reverse():将拼接的字符串进行反转。
	toString(): 打印字符串。

8、StringBuffer类

1、它是:
	java.lang 包下的类

2、作用:
	它也可以进行字符串拼接,

3、区别:
	(1)StringBuffer类是线程安全的,StringBuilder线程不安全【不能同步访问】,但是速度快。
	(2)然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

9、包装类

1、作用:
	将基本类型转换成对象。方便操作。

2、基本类型				包装类
		byte			Byte
		short			Short
		int 			Integer
		long			Long
		float			Float
		double			Double
		char			Character
		boolean		Boolean

3、包装类常用方法(除Character类。其余的可以将字符串转换为自己的那种类型)
		toString() : 将某种类型转换为字符串。
		parse类型() : 将字符串转换为某种类型。例如:parseByte()、parseShort()。

4、装箱和拆箱的好处。
	(1)装箱:将"基本类型"赋值给“包装类型”。
	(2)拆箱:将"包装类型"赋值给"基本类型"。
	(3)好处:将"包装类型"与"基本类型"可以等同看待,操作起来比较方便。

二、集合

1、集合概述

1、作用:	
	集合是一个容器用来存储数据。它只能保存引用类型 (也就是只能保存对象,基本类型通过包装类来完成)

2、它和数组的区别:
		(1)数组:长度固定的。集合:长度不固定。
		(2)数组中存储的是同一类型的元素,,可以存储基本数据类型值。集合储存的都是引用类型。对象的类型可以不一致。
		
3、集合的分类(两大类)		
	(1)单列集合 【Collection】 
	(2)双列集合	【Map】

3、常见的数据结构
	(1)数组:ArrayList
	(2)链表:LinkedList
	(3)哈希表:HashSet【它地层是:数组+链表+红黑树实现】
	(4)栈
	(5)队列
	(6)树
	(7)图

2、数据结构

1、介绍:
	数据结构存是指储数据的方式。不同的存储方式,会影响增、删、改、查操作的效率。

2、数据结构介绍:
	(1)数组:
		a、地层原理:内存中是“连续的”,通过索引访问;
		b、特点:查询快;增删慢。
	
	(2)链表:
		a、地层原理:多个节点通过记录地址连接的。
		b、特点:查询慢;增删快。
    
    (3)哈希表特点:
    		1).不存储重复元素;
			2).JDK8后,使用的是数据结构:数组 + 链表 + 红黑树;
			3).验证元素唯一性的方式:
				1).先比较元素的hashCode()
					不同:存
					相同:比较元素的equals()
						不同:存
						相同:不存
    	
    (4)栈:
     	a、地层原理:添加、删除都在同一端;(1)压栈:就是存元素。(2)弹栈:就是取元素。
     	b、特点:先进后出。	
	
	(5)队列:
		a、地层原理:添加、删除各占一端。
    	b、特点:先进先出。
	

	(6)	树:【红黑树(二叉树)】
			就是一种类似于我们生活中树的结构,只不过每个结点上都多只能有两个子结点。二叉树是每个节点多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。

    	

3、Collection(单列集合)

1、介绍:
	(1)它是单列集合类的根接口,他有两个重要的子接口List、Set。
	(2)List的特点:是元素有序、元素可重复。
	(3)Set的特点:元素无序,而且不可重复。

2、单例集合通用的方法:
	add(): 添加到当前集合中。(添加到集合的尾部)
	clear() :清空集合中所有的元素。 
    remove() :从集合中删除。(注意:如果有多个相同元素,只移除一个)
    contains():判断当前集合中是否包含给定的对象。 
    isEmpty():判断当前集合是否为空。 
    size() :返回集合中元素的个数。 
    toArray() : 把集合中的元素,存储到数组中。
List集合
1、特点:
	(1)存储的元素是有序的;(2).可以存储重复元素。

2、List接口中新增对索引操作的方法:
	add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上
	get(int index) :返回集合中指定位置的元素。 
	remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。 
	set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
	
3、它的实现类
	ArrayList、LinkedList
	
4、ArrayList集合特点:
	 存储的结构是数组结构。元素增删慢,查找快。

5、LinkedList集合:
	存储的结构是双向链表结构。方便元素添加、删除的集合。(它也可以作为堆栈,队列的结构使)。

6、LinkedList对首尾操作的特有方法:
	 addFirst() :将指定元素插入此列表的开头。
	 addLast() :将指定元素添加到此列表的结尾。 
	 getFirst() :返回此列表的第一个元素。
	 getLast() :返回此列表的后一个元素。
	 removeFirst() :移除并返回此列表的第一个元素。 
	 removeLast() :移除并返回此列表的后一个元素。
	 poll() :弹栈从集合中取出数据。
	 push(E e) :压栈装入集合。
	 boolean isEmpty() :如果列表不包含元素,则返回true。

Set集合
1、特点:
	(1)存储的元素是无序的;
	(2)不能存储重复元素;

2、子类(实现类)
	HashSet(哈希表实现)、LinkedHashSet(链表、哈希表实现,它是特例:它是有序的,是它自己的顺序。)。
	
3、哈希表:
	在JDK1.8之前,哈希表采用地层采用的是数组+链表。JDK1.8中使用的是数组+链表+红黑树实现。是先判断元素的hashCode(),再判断元素的equals()。不同在进行存储。
	

4、Collections单例集合工具类

1、作用:
	里面包含了对Collection集合操作的一些工具性方法。
2、使用:
	(1)java.util.Collections(工具类)包下。

3、常用的静态方法:	
	addAll(集合名, 参数1,参数2......) :往集合中添加一些元素。 
	shuffle(集合名) 打乱顺序 :打乱集合顺序。
     
	sort(集合名) :将集合元素进行默认排序。
	 	(1)注意:对集合元素排序,是基于元素的compareTo()方法排序。(必须实现Comparable接口)
	
	sort(集合名,new Comparator<? super T> :将集合中元素按照指定规则排序。
	 	(1)注意:使用“比较器”对集合元素进行排序。使用匿名内部类来完成
			

5、比较器

1、比较器Comparable和Comparator的区别:
		他们都是比较器。作用一样。用法不一样。


2、Comparable(接口):
		(1)特点:
			强行对它的实现类的对象进行整体排序。这种排序被称为类的自然排序。类的compareTo方法 被称为它的自然比较方法。
		(2)缺点:
			在类需要实现Comparable接口。使用compareTo()方法进行排序,排序的灵活性被限制。只能是这一种排序方式。
3、Comparator(接口):
		(1)特点:
			强行对某个对象进行整体排序。可以将Comparator 传递给sort方法。
		(2)好处:
        	(1)使用集合工具类的sort方法,进行自定义对象排序的时候,自定义类不用继承任何类。它可以直接将对象传进去进行排序。
            (2)更灵活。对对象排序不用动源码。

6、Map(双列集合)

Map概述
1、特点:
		1).Map内部采用“键值对”的形式存储;
		2).在一个Map内,“键”是唯一的;
		3).键和值可以是任何对象;
		4).可以通过“键”获取对应的“值”;
2、它的常用实现类:
	注意:Map集合的存储结构只对“键”进行处理。值不进行处理。
	
	HashMap(子类):键是“哈希表”结构。
	LinkedHashMap(子类):键是“链表、哈希表”结构;【通过链表结构可以保证元素的存取顺序一致】

3、Map内部类Entry介绍:
		它是Map的内部类,将Map中的每一个键值对,进行一一对应的封装成一个对象。【其实就是Map中的每一个键值对就是一个Entry对象】
	
3、方法:
	(1)添加、修改(使用的同一个方法):
	 	put():添加。【如果存储的键相同,新值‘替换’旧值,并返回旧值】
	(2)删除:
		remove(Object key):使用键删除对应的“键值对。【将键和值都进行删除】
	(3)查询:
    	get(K key):通过键或取对应的值。
    	size():获取键值对的数量。
    	keySet() :获取Map集合中所有的键,返回类型是Set集合。
        entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
    (4)遍历:
    	注意:Map本身没有遍历的方法,都是间接遍历。
    	1).键找值:keySet()。获取所有的的key进行遍历。根据key获取值。
    	2).获取“键值对所有的对象”的方式;entrySet()。
HashMap
1、注意事项:
	自定义对象做键,自定义对象必须重写hashCode()和equals()方法。不然键重复也能存进去。

2、执行原理:
	因为Map地层存储的是Entry对象。每个对象在内存中堆中地址是不一样的。不重写hashCode()和equals()方法。那么它只是比较地址是否相同。不同进行存储。重写后会进行比较对象里面的值是否一样。
LinkedHashMap
1、注意事项:
	它是Map的特例,有序的哈希表;储存的顺序有序。

7、JDK9对集合添加的优化

1、介绍:
	JDK9对List、Set、Map集合提供了一种静态方法:of() 。
2、例如:
	List<String> strList = List.of("张三","李四","王五","周六");
	Set<String> strSet = Set.of("黄渤","黄老邪","黄磊","黄瓜");
	Map<Character,Integer> map = Map.of('a',10,'b',20,'c',25);
3、特点:
	它们返回都是:(长度)不可变的集合。不能对元素进行添加、删除等操作;

三、泛型

1、概述和使用

1、介绍:
	可以在类或方法中预支地使用未知的类型。泛型用来规范集合用的。

2、泛型的好处:
	(1)让编译器为我们的代码监督,只能向集合中添加同一种类型的数据,
	(2)泛型,可以用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
	(3)什么时候使用什么时候确定泛型。
3、使用:
	(1)泛型类的定义格式: 
		修饰符 class 类名<代表泛型的变量> {  }
	
	(2)含有泛型的方法:
    	修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }
	
	(3)含有泛型的接口 :
		修饰符 interface接口名<代表泛型的变量> { }
		
4、泛型的统配符:
	<?> : 表示可以接收任何泛型的集合。(注意:使用泛型统配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用)
	
5、泛型的上限、下限。
	(1)上限:
		格式:
			类型名称 <? extends 类 > 对象名称 。
		意义:
			只能接收该类型及其子类。
	(2)下限:
		格式:
			 类型名称 <? super 类 > 对象名称 。
		意义:
       		 只能接收该类型及其父类型 。

四、异常

1、介绍

1、异常介绍:
		是指程序在“运行时”。JVM遇到了无法处理的数据,或者表达式。这种情况就叫:异常。
	
2、异常体系:
	Throwable(顶层的异常父类)
		|--Error(错误)异常中不建议捕获的重大错误;例如:内存溢出等。
		|--Exception(异常)希望我们程序捕获并处理的异常情况,处理后程序可以继续运行下去。
			|--RuntimeException(运行时异常)程序可以不处理。
			|--除RuntimeException外其它的异常类都是(编译期异常)程序必须处理,否则编译错误。

3、虚拟机处理异常的方式:
	1).JVM执行到有异常的代码;
	2).JVM要识别出这种异常;
	3).JVM到类库中找到对应的异常类,并创建对象;
	4).判断代码中是否捕获(try catch)这种异常
			是:执行catch进行捕获
			否:在控制台打印异常信息,结束程序。

2、常见的异常

1、空指针异常:NullPointerException(运行时异常)
2、数组下标越界异常:ArrayIndexOutOfBoundsException(运行时异常)
3、数字转换异常:NumberFormatException(运行时异常)
4、算术运算异常:ArithmeticException(运行时异常)
5、转换异常:ParseException(编译期异常)--必须要捕获,否则编译错误

3、异常处理

1、异常的捕获:
	try{
      //可能出现异常的代码
	}catch(异常类名 变量名){
      //如果try中出现了“异常类名”的异常,这里会被捕获,
      //会执行这里的代码。
	 //catch可以有多个但是有异常只会执行一个。
	}finally{
      //不管有没有异常都会执行。
      //它一般情况可以不要。
	}

2、声明抛出异常【throws】
		表示“声明”此方法可能会产生某种异常,如果产生,请JVM将此异常对象抛给调用者。如果没有产生异常,则正常执行完毕。
		
3、抛出异常【throw】
		表示立即“抛出一个异常对象”。

4、自定义异常

1、自定义异常注意事项:
	(1)自定义异常类必须继承Exception或者它的某个子类 (RuntimeException/非RuntimeException(建议));
	(2)建议:添加一个String参数的构造方法,用于设置“异常信息”	
2、格式:
	public class AgeException extends Exception{
    	public AgeException(String msg) {
        	super(msg);
		}
	}

五、多线程

1、概述

1、进程概念:
	什么是“进程”:“进程”是操作系统的概念,一个独立运行的程序,就是一个“进程”。
2、线程的概念:
	什么是“线程”:“线程”是由“进程创建”的,一个进程可以创建任意多的线程,每个线程都包含一些代码。线程中的代码会同主线程或者其他线程“同时运行”。

3、什么是“多进程:
	在某个时间段内,同时运行多个程序。
4、什么是“多线程”:
	一个程序可以同时启动多个线程,也就意味着可以使多段代码“同时运行”,可以提高程序的运行效率。

5、并行:
	多个线程同时开始运行。

6、并发:
	多个线程同时访问同一资源(CPU)。

7、进程与线程的区别:
	进程:有独立的内存空间,进程中的数据存放空间(进程:堆空间、线程栈空间)是独立的,它至少有一个线程。
	线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

8、注意事项:
	(1)线程并发运行其实是有先后顺序的,那个线程先执行那个线程后执行,完全取决于电脑的CPU调度。
	(2)java的进程至少包含两个线程。主进程main()方法,和垃圾回收机制线程。每当java执行一个类时,都会启动一个JVM,能JVM就是一个线程。Java本身具备了垃圾的收集机制。所以在 Java 运行时至少会启动两个线程。

9、线程的调度:
	JVM采用的是抢占式调度,没有采用分时调度, 因此可以能造成多线程执行结果的的随机性。 

2、多线程运行的原理

	每一个线程在内存中,都有一个独立的栈空间。进行方法的压栈弹栈。当执行完线程执行完之后,线程自动在栈内存中释放。当执行的所有线程结束了,那么进程就结束了。

3、Thread 类

1、作用:
	 它是java提供线程类,使用线程必须继承它。
2、使用格式:
	(1)它是java.lang包下的
	(2)自定义线程类,需要继承自Thread,并重写run()方法:。run方法中是线程要执行的代码。
	(3)在调用处使用start()启动线程;
3、注意事项:
	(1).重写的是run(),但启动线程是:start();
	(2).对于“一个线程对象”,只能调用一次start();
	(3).对于“一个线程类”,可以创建多个线程对象,每个线程对象都可以以独立的。

4、构造方法:
	Thread() :分配一个新的线程对象。 
	Thread(String name) :分配一个指定名字的新的线程对象。
	Thread(Runnable target) :分配一个带有指定目标新的线程对象。
	Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

5、常用方法:
	 getName() :获取当前线程名称。
	 start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
	 run() :此线程要执行的任务在此处定义代码。 
	 sleep(long millis) :让当前的线程进行休息多长时间。
     currentThread() :返回对当前正在执行的线程对象的引用。 

4、Runnable接口

1、作用:
	实现它重写run方法。也可以作为线程。但是它不是线程。还需要借助Thread线程类。
2、使用:
	(1)它是java.lang包下的

3、使用格式:
	(1)创建类实现Runnable接口重写run()方法。
	(2)创建Thread类构造方法参数是(实现Runnable接口的自定义类)
	(3)启动使用start()方法。

4、注意事项:
	(1)自定义类实现了Runnable接口,并没有继承自Thread,所以不是一个“线程”。
	(2)这种方式仍然需要Thread。
	(3)可以将一个自定义类的对象,传给多个Thread。

5、Thread、Runnable的区别

1、区别:
	(1)第一种方式需要子类继承自Thread。由于Java是“单继承”,所以对子类造成了不方便。
	(2)第二种方式需要子类实现Runnable接口。Java允许一个类同时实现多个接口,所以对于子类。

6、匿名内部类实现线程

//方式一:Thread的匿名子类
    new Thread(){
      	//线程执行的代码块
    }.start();
//方式二:Runnable的匿名子类
	new Thread(new Runnable(){
       //线程执行的代码块
    }).start();

7、线程同步

1、容易出现的问题:
	当多线程共同访问同一共享资源时,就会产生:安全性问题。例如:多个线程队全局变量进行++处理就会出现问题。
2、解决方案:
	(1)同步锁【使用synchronized 关键字来完成的】:
		同步锁只是一个概念。意思就是给一个对象添加一个标记,表示锁的意思。谁先拿到谁先执行。等它执行完之后下一个在进行执行。【就是多个线程使用同一把锁。】	
	(2)Lock锁:它java提供了比synchronized代码块和synchronized方法更广泛的锁。
    
3、同代码块作用:【synchronized 关键字】
	在方法中的某个区块使用synchronized关键字,进行对这个区块的资源实行互斥访问【使用它可以完成多线程同步锁。】

4、使用格式:
	//注意使用在方法中
	synchronized(同步锁【同步锁:可以是任何】){
    	//需要同步操作的代码
	}

5、Lock锁作用:
	(1)它是一个接口。需要使用它的实现类。;
	Lock锁也称同步锁,加锁与释放锁方法进行了简化。比使用synchronized关键字完成同步锁更加的简单。

6、Lock锁常用方法:
	lock() :加同步锁。 
	unlock() :释放同步锁。

7、使用格式:
		Lock l = new 它的实现类; 
			l.lock(); //加锁
		try { 
			//代码块 
		} finally { 
			l.unlock(); //解锁
		} 

8、线程的状态

1、新建:表示刚创建线程
2、可运行或者待运行:表示线程在java虚拟机中正在运行自己的代码,也可能没有运行。
3、锁阻塞: 当一个线程试图获取一个对象锁,但是对象锁被其他线程拿走了,那么该线程进入等待转态。
4、无限等待:一个线程在等待另外一个线程(唤醒)。这等待的过程就叫做无限等待。
5、计时等待:运行后,被调用了sleep()方法;
6、被终止:run()方法正常运行完毕。或者有异常导致run方法死亡。

9、线程等待与唤醒使用

1、作用:
	就是一个线程在等待状态不能允许,需要另外一个线程唤醒后才能运行。

2、使用的方法:
	锁对象.wait():表示等待唤醒,进入无限等待状态。
    锁对象.notifyAll():表示唤醒当前持有本锁的线程
  
3、等待唤醒机制的通信:
	(1)当一个工作需要多个线程同时配合完成,这样就需要多个线程之间进行“通信”,以保证这个工作能够完整的完成。这种相互配合的方式:线程间通信。
	(2)“等待与唤醒机制”就是“线程间通信”的一种体现。
	(3)例如:
		1).一个线程做一些“准备性工作”。
		2).另一个线程做正常的工作。
			(1)由于两个线程是“无序的”,很有可能“第二个线程工作时,第一个线程的准备工作还没有做好,
			(2)这时就需要第二个线程要主动“等待”
			(3)然后第一个线程进入开始做准备,准备工作做好后,
			(4)再唤醒第二个线程开始正常工作。

10、线程池

1、线程池的概念:
	(1)线程池:就是存储了若干多的“线程对象”的一个“容器。
	(2)对于一个线程对象“只能启动一次”,若想第二次再用,就需要再次创建一个线程对象,但如果创建线程对象很耗时。这样如果程序中要反复的使用同一个线程,整个程序的效率就会很低。
	(3)线程池的思想:在程序启动时,会先创建若干多的线程对象,并存储到一个容器中,作为“线程池”。如果有需要时,取出一个线程对象,并执行它。执行完毕,线程池会回收这个线程对象。如果再次需要,可以再次取出,并执行它。所以,线程池中的线程对象是“可以反复重用”的。这样就避免的反复的创建线程对象,从而提高程序的执行效率。

11、ExecutorService线程池类

1、介绍:
	java里面线程池的顶级接口是 Executor 、,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 ExecutorService 。

2、Executor:
	java在Executor线程工厂类里面提供了一些静态工厂,可以生成一些常用的线程池。

3、ExecutorService:
	它是线程池类。通过Executor静态工厂进行生产的。

4、方法:
	newFixedThreadPool((int nThreads):返回线程池对象。参数是线程池里面线程的存储数量。
	submit(Runnable task):获取线程池中的某一个线程对象,并执行 。第一次进行缓存。
	shutdown():关闭线程池。

5、线程池的创建和使用:
		//使用线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        //创建线程对象
        MyThread t = new MyThread();//5秒
        //第一次执行此线程--交给"线程池”去执行,线程池执行后,会将此线程对象"缓存",可以重用
        service.submit(t);
        //第二次执行此线程【从线程池中获取】
        service.submit(t);
        //第三次执行此线程【从线程池中获取】
        service.submit(t);
        //关闭线程池
        service.shutdown();

六、函数式编程Lambda

1、Lambda概念

1、作用:
	Lambda是匿名内部类的简写。
	
2、简化的变化过程:	
	内部类 ——> 匿名内部类 ——> Lambda表达式 ——> 方法引用。【作用:都是内部类】	
	
3、Lambda表达式思想:
	(1)将编程思想转换成“函数式”编程思想。也就是“以什么形式做”,转换为“怎样做”。
	(2)Lambda更关注的是“怎么做”,不是以“什么形式去做”。 
	(3)优点:语法简洁。

4、Lambda表达式使用前提:
		必须是函数式接口。

2、Lambda的使用

1、标准格式:
		1).一对小括号--形参
		2).一个箭头:->
		3).一对大括号--方法体
2、省略格式:
		1).形参:Lambda中的"形参类型”都可以省略;
		2).形参:如果只有一个形参,形参类型和小括号都可以省略。【注意:要省略小括号,必须省略数据类型】
		3).方法体:如果方法体中只有一条语句,可以使用省略规则。
			A).有返回值:可以省略大括号、return关键字、语句后的分号【注意:要省全省,要用全用】
			B).无返回值:可以省略大括号、语句后的分号;【注意:要省全省,要用全用】

3、函数式接口

1、函数式接口:
		有,且“只有一个抽象方法” 的接口叫:函数式接口。

2、@FunctionalInterface注解:该注解可用于在一个接口上。
		一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

4、方法的引用

1、理解:
	使用已有的方法。来完成对函数式接口中的抽象方法的重写。也就是“方法代替”。(使用已有的方法代替函数中的抽象方法)	

2、方法引用的要求:
		引用的方法”的形参、返回值类型,必须与被替换的"函数式接口”中抽象方法的一致。

3、引用表达式:
	双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。 

4、好处:
	(2)代码更简介。
	(3)开发效率高。

5、静态方法的引用

1、使用格式:
	类名::方法名。

2、注意事项:
	(1)必须通过"类名"引用,不能通过"对象名"引用。
	(2)格式:
			类名::方法名。	

6、成员方法的引用

1、使用格式:
	格式一:
		类名  对象名=new 类;
		对象名::方法名。
	格式二:
    	new 类 ::方法名  	

7、super、this的方法引用

1、注意:
	super、this只能用在子类中。

2、使用格式:
		super::方法名;//表示使用引用父类的方法。
		this::方法名;//表示使用引用当前类的方法。

8、构造方法的引用

1、无参构造方法的引用格式:
		类名::new //相当通过引用当前类的无参构造方法,创建一个无参构造方法的对象。
2、有参构造方法的使用:		
		类名::new,参数 //表示通过构造参数创建一个有参构造对象。

9、常用函数接口

1、Supplier生产
1、生产者接口Supplier作用:
	(1)可以用作lambda表达式或方法引用的赋值对象。
	(2)没有要求每次调用供应商时都会返回新的或不同的结果。
	
2、特点:
	1.只生产数据,不接收参数。在里面操作完之后,返回一个值。

3、使用格式:(使用它需要一个传递一个引用类型)
	 Supplier <类型>  //表示:使用它当作方法的参数,那么 <> 中的类型是什么它就是返回什么类型的参数。
4、抽象方法:
	T get();
2、Consumer消费
1、消费接口Consumer作用:
	(1)可以用作lambda表达式或方法引用的赋值对象。
	(2)	表示接受单个输入参数并且不返回结果的操作。 
2、特点:    
    只接受参数,无返回值。

3、抽象方法:
	accept():只接受参数,无返回值。

4、默认方法:
	andThen():将两个Consumer对象的accept()方法的结果连在一起。

5.使用格式:
	Consumer <类型> //使用它当作方法的参数。那么参数是什么 ,<> 中的类型就写什么。
	参数格式例如:Consumer <String>  con,String s
3、Predicate判断
1、作用:
	(1)因此可以用作lambda表达式或方法引用的赋值对象
	(2)对某种类型的数据进行判断,得到一个boolean值结果。

2、抽象方法:
	trst():返回boolean类型。

3、默认方法:
	and():判断两个Predicate是否,是并且关系:
	or(): 或者
	nwgate(): 非

4、使用格式:	
	Predicate <类型> //使用它当作方法的参数。那么参数是什么 ,<> 中的类型就写什么。
	 参数格式例如:Predicate<String> p , String s
	
4、Function转换
1、作用:
	(1)因此可以用作lambda表达式或方法引用的赋值对象。
	(2)表示接受一个参数并产生结果的函数。
	(3)通常用来做转换。接受一种类型,转换成另外一种类型。
2、抽象方法:
	 apply(T t): 将此函数应用于给定的参数。  

3、默认方法:
	andThen():将两个apply方法连接起来。

4、使用格式:	
		Function<类型1,类型2>//将类型1,转成类型2。

10、延迟方法、终结方法

1、概述:
	是指函数式接口中的延迟方法和终结方法。
2、区别:
	(1)延迟方法:只是在拼接Lambda函数模型的方法, 并不立即执行得到结果。
	(2)终结方法:根据拼好的Lambda函数模型, 立即执行得到结果值的方法。
3、备注:
	JDK中更多内置的常用函数式接口,请参考 java.util.function 包的API文档。

11、Stream流

1、概述
1、在Java 8中,得益于Lambda所带 来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

2、简单的理解:
	 它就是一个对集合进行操作的“工具类”。里面包含很多对集合操作的方法。尤其是进行筛选、过滤、求总数量....等一系列操作时。
			使用Stream流 + Lambda表达式,就非常的方便。

2、Stream流的获取
1、通过集合对象获取Stream流:
	(1)List集合获取流:
			List<String> list = new ArrayList<>();
			//获取流
			Stream<String> s = list.stream();
	(2)Set集合获取流:
    		Set<String> set = new HashSet<>();
    		//获取流
    		Stream<String> s = set.stream();
    (3)Map集合获取流:
    		Map<String,String> map = new HashMap<>();
    		//Key获取流
    		Stream<String> keyStream = map.keySet().stream();
    		//Value获取流
    		String<String> valueStream = map.values().stream();
2、通过“数组”获取流:
	(1)零散的数据:
		A、零散数据这里是指,用 Stream 接口的 of()方法来获取零散的数据,它不是集合也不是数组,是单个的数据,通过整理也能获取流,对它们进行操作。
			Stream<String> s1 = Stream.of("a", "b", "c", "d");
			
	(2)通过"数组"获取流
			Integer[] arr = {10, 20, 30, 40};//必须是引用类型数组
			Stream<Integer> s2 = Stream.of(arr);
	(3)基本类型数组:
             int[] arr2 = {10, 20, 30, 40};
             IntStream s3 = IntStream.of(arr2);

3、常用方法
1、使用注意事项:
	(1)流的延迟方法还会返回流本身,还可以继续调用,可以使用方法链。
	
	(2)终结方法:
	  	方法执行完后,返回的值类型不再是Stream流类型,因此不再支持类似StringBuilder那样的链式调用。
    (3)延迟方法:
    	返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。

2、终结方法:
	count() : 统计个数。
	forEach(): 逐个处理。
3、延迟方法:
	filter():过滤。返回对错
	limit():取前几个。
	skip():跳过前几个。
	

七、IO流

1、File类

1、作用:
	它可以表示磁盘上的一个文件或者目录。使用它可以获取磁盘上的文件/目录的一些属性信息。
		例如:
			1).文件/目录名;
			2).文件的大小;
			3).文件/目录的最后修改时间;
			4).文件/目录的是否可读、是否可写。
2、构造方法:
	File file1 = new File(绝对路径);

3、File类_绝对路径和相对路径
	1).绝对路径:带盘符的全路径;
	2).相对路径:不带盘符的。从项目根目录下找。

4、获取的方法:
	getAbsolutePath():返回此File的绝对路径名字符串。
	getPath():获取此File对象封装的路径。
	getName() :返回由此File表示的文件或目 录的名称。
	length():返回由此File表示的“文件”的长度。单位:字节【注意:不能操作目录】

5、判断的方法:
	exists():此File表示的文件或目录是否实际存在。
	isDirectory():此File表示的是否为目 录。
	isFile():此File表示的是否为文件。

6、创建和删除的方法:
	createNewFile():如果该名称的文件不存在, 创建一个新的空文件。
	delete():可以删除“文件”和“空目录”【非空目录使用递归】。
	mkdir():创建单级目录。
	mkdirs():创建多级目录。

7、目录的遍历方法:
	 list() :返回一个String数组,表示该File目录中的所有子文件或目录。
	 listFiles() :获取此目录下所有的文件/目录的File对象数组。
	 注意:【listFiles在以下三种情况下会返回null:】	
	 	(1)当前的File对象表示的是一个文件。
	 	(2)当前的File对象表示的是一个“系统目录”。
	 	(3)当前的File对象表示的目录不存在。
	 	(4)如果当前File对象指向的是一个存在的空目录,listFile()不返回null,而是一个零长度数组。

2、递归

1、作用、理解:
	方法调用其本身;【当前的方法调用当前的方法】
2、注意事项:
	1).递归的层次不能太深,否则“栈溢出”。
	2).必须要有出口,否则就是“死递归”。
	3).构造方法不能递归调用。

3、递归内存溢出隐患的原因:
	递归会一直压栈,不弹栈,就会最终导致“栈溢出"。

3、IO流概念

1、介绍:
	IO流:可以“读”,“写”文件的内容。
		输入流 :把数据 从其他设备上 读取到 内存 中的流。 
		输出流 :把数据 从内存中写出到 其他设备 上的流。

4、Java的IO体系结构

1、字节流:
	1).输入流:InputStream(抽象类)
				|--FileInputStream(字节基本流)
				|--FilterInputStream(不学)
					|--BufferedInputStream(缓冲流)
					
	2).输出流:OutputStream(抽象类)
				|--FileOutputStream(字节基本流)
				|--FilterOutputStream(不学)
					|--BufferedOutputStream(缓冲流)
				

2、字符流
	1).输入流:Reader(抽象类)
				|--BufferedReader(缓冲流)		
				|--InputStreamReader(转换流)
						|--FileReader(字符基本流)
				
	2).输出流:Writer(抽象类)		
                 |--BufferedWriter(缓冲流)    
                 |--OutputStreamWriter(转换流)
    					|--FileWriter(字符基本流)

5、字节流

1、特点:
	(1)以字节为单位,读写数据的流。
	(2)“字节流”是Java中IO流的最基本的流,任何的其它流都是基于字节流的【例如:字符流、打印流】。   

6、字符流

1、特点:
	(1)以字符为单位,读写数据的流。
	(2)它是java为了解决各个国家使用的编码表不同,字符占用的字节数不同。提供的另一种专门操作纯文本文件的流

7、缓存流

	缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO 次数,从而提高读写的效率。 

8、字符转换流

	解决编码格式的一种流。例如:编码使用的GBK,解码使用UTF-8。就会出现乱码的格式。

9、打印流

	1).java中有两种打印流:
		字节打印流:PrinteStream
		字符打印流:printWriter
	2).打印流的特点:
		1).只有输出流,没有输入流;
		2).可以操作控制台,也可以操作文件;

9、常见的字符集

	(1)ASCII码表:最早的码表。记录了128个字符,每个字符使用1个字节表示。
	(2)中文的码表:【都是以GB开头】
			1).GB2312:早期的码表。
			2).GBK : 目前使用的码表。里面一共记录了2万多个汉字。
			3).GB18030 : 里面有7万多汉字
	(3)UTF-8码表:国际码表。
	(4)Unicode码表:国际码表。 Java就是支持Unicode码表的。
	(5)ISO-8859-1码表:没有中文。

10、序列化

1、序列化:
	将一个“对象连同属性值”一起存储在一个文件中。或者通过网络传输,这个过程叫:序列化。
2、反序列化:
	指将之前序列化的对象,再次读取到程序中,并在内存中创建对象。

3、一个对象要想序列化,必须满足两个条件:
		(1)该类必须实现 Serializable 接口。
		(2)该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用 transient 关键字修饰。


4.反序列化
	(1)对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。 
	(2)另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操 作也会失败,抛出一个 InvalidClassException 异常。发生这个异常的原因如下:
			(1)该类的序列版本号与从流中读取的类描述符的版本号不匹配 。
			(2)该类包含未知数据类型。
			(3)该类没有可访问的无参数构造方法 。
			

八、网络编程

1、概述

1、程序的架构:
	C/S结构 :全称为Client/Server结构, 是指客户端和服务器结构。 常见程序有QQ、斗地主、网游......
	B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。 常见浏览器有谷歌、火狐、等。

2、网络编程的三要素:
	(1)IP地址
	(2)端口
	(3)协议

2、IP地址

1、作用:	
	指互联网协议地址,俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。

3、端口

1、作用:
	网络的通信。是指两个进程之间的通信。端口号的唯一标识符,可以识别设备中的具体进程。端口具体分:两类。

2、物理端口:网卡口。

3、逻辑端口:
	(1)由操作系统维护,就是一组数字(唯一标识符)。范围:0--65535。
	(2)任何程序在启动时都可以自行申请端口,如果这个端口没被占用,操作系统就可以分配给你的程序使用
	(3)注意:
		(1)一个程序启动后可以同时占用多个端口。
		(2)一个端口在某个时间点上不能被多个程序同时占用

4、协议

1、主要分两类:
	 网络通信协议、TCP/IP协议。

2、网络通信协议:
	 通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。这就 好比在道路中行驶的汽车一定要遵守交通规则一样,协议中对数据的传输格式、传输速率、传输步骤等做了 统一规定,通信双方必须同时遵守,最终完成数据交换。

3、TCP/IP协议:【传输控制协议/因特网互联协议】
	 它是 Internet最基本、最广泛的协议。定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它 的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的 协议来完成自己的需求。
		应用层:HTTP、FTP、TFTP、SMTP、SNMP、DNS。
		传输层:TCP、UDP
	
		网络层:ICMP、IGMP、IP、
		数据连接层/物理层:由地层网络定义的协议。

5、传输层协议TCP、UDP

1、TCP协议:【传输控制协议 】
	(1)	TCP协议是面向连接的通信协议即传输数据之前, 在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
	(2)连接流程(三次握手,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。)
			第一次握手,客户端向服务器端发出连接请求,等待服务器确认。 
			第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。 
			第三次握手,客户端再次向服务器端发送确认信息,确认连接。
	
	(3)完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可 以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
	
	
2、UDP协议:【用户数据报协议】
	(1)UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。
	(2)每个数据包的大小限制在64k以内。
	(3)它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。

6、TCP通信要求

1、TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。

2、两端通信时步骤:
	1. 服务端程序,需要事先启动,等待客户端的连接。 
	2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。

7、Java的TCP通信程序

1、介绍:
	在Java中,提供了两个类用于实现TCP通信程序。
	
2、客户端:
	java.net.Socket类表示客户端。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。 	

3、服务端: 
	java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端的连接。 

8、Socket类

1、作用:
	客户端建立连接服务端

9、ServerSocket

1、作用:
	建立一个端口等待连接

10、执行流程

1. 【服务端】启动,创建ServerSocket对象,等待连接。
2. 【客户端】启动,创建Socket对象,请求连接。
3. 【服务端】接收连接,调用accept方法,并返回一个Socket对象。
4. 【客户端】Socket对象,获取OutputStream,向服务端写出数据。 
5. 【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。
6. 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
7. 【客户端】Scoket对象,获取InputStream,解析回写数据。 
8. 【客户端】释放资源,断开连接。 

九、反射

1、概述

1、作用:
	“反射”是指“反向加载”要使用的“类”。这种技术可以解决这种类和类之间的依赖关系。
	
2、注意:
	(1)JVM在第一次使用任何类时,都会在内存中创建一个这个类的Class对象。而且针对每个类,都只有这一个Class对象。【理解:JVM启动的时候会把所有的类进行加载到内存中】
	
	(2)Class类:就是Class文件。每一个Class文件都是Class类的一个实例对象。
	
3、反射工作机制:
	(1)首先获取某个类的Class文件。就是让JVM先把这个类Class对象先创建了。
	(2)然后JVM加载Class文件。
	(3)加载完成Class文件之后,存储到内存中(方法区)。
	(4)存储到内存中后,它会直接把Class对象返回给我们的程序。
	(5)我们获取到Class对象,就想到于获取到这个对象。

3、特点:
	解决类与类之间的耦合。实现高内聚、低耦合的理念。
	

2、Class对象

1、获取Class对象的三种方式:
	1).getClass()方法--Object类中定义的;
	2).任何的数据类型(基本类型、引用类型)都有一个静态属性:class
	3).Class类的静态方法:forName(全名限定的类名)
		注意:三种方式获取的都是同一个Class对象;而且每种方式都是先判断Class对象是否存在,
														如果不存在,都会先创建一个Class对象。

2、获取构造方法	
		getDeclaredConstructor()获取某个构造方法。
		getDeclaredConstructors():获取所有的构造方法,包括公有、受保护、默认、私有的。

3、爆力访问:
	使用场景:有私有的参数时使用。//由于是私有的构造方法,所以要先设置暴力访问。
    	setAccessible(true);

4、通过构造方法创建对象:
		newInstance();
		
5、获取成员属性:	  
		getDeclaredField(String fieldName):获取某个成员属性。
		getDeclaredFields():获取所有成员属性。	
		注意:私有的成员属性要先设置暴力访问:对象.setAccessible(true);
		
6、给成员属性赋值:
		set():赋值。
		注意:私有先暴力访问。对象.setAccessible(true);

十、注解

1、概述

1、介绍:
	注解就是一种“标记”。给编译器(JVM)看到。编译器(JVM)根据注解来完成对应的功能。

2、作用:
	用来告诉“编译器”怎样编译“代码”。
	
3、注解的属性:
	(1)可以让用户在使用注解时传递参数。
	(2)格式:
		格式一:数据类型 属性名();
		格式二:数据类型 属性名() default 默认值; 
		
4、特殊属性value 
	 (1)当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值,无论value是单 值元素还是数组类型。
	 (2)如果注解中除了value属性还有其他属性,且至少有一个属性没有默认值,则在使用注解给属性赋值时, value属性名不能省略。


5、自定义注解:@interface 
	(1)格式:
		public @interface 注解名{
			
		}
	(2)注意事项:
		1)这是一个合格的注解。但是它没有任何功能。可以使用在任何地方。
		2)如果想要它有功能,需要我们编写“注解解析器”
		3)如果只想让这个注解在规定的地方使用。需要元注解。

2、元注解

1、介绍:
	(1)元注解是Java API提供的注解。专门用来定义注解的注解。【自定义注解】。它规定了定义的注解可以用在哪些位置,还有注解会存在于哪里。
	(2)已经定义好的元注解有两种:
		
		@Target:规定了注解可以用在哪些位置。
		@Retention:规定了注解会存在于哪里。
		
		
2、@Target:
	(1)作用:
		(1)它规定了注解可以用在哪些位置。通过它的选择它的属性值来进行规定注解用在那些地方。
		(2)它的选择值被定义在java.lang.annotation.ElementType枚举中。
	(2)使用:
			@Target(ElementType.TYPE):可以用在类,接口中
			@Target(ElementType.FIELD):可以用在字段上(成员属性)
			@Target(ElementType.METHOD):可以用在方法上
			@Target(ElementType.PARAMETER):可以用在参数上
			@Target(ElementType.CONSTRUCTOR):可以用在构造方法是哪个
			@Target(ElementType.LOCAL_VARIABLE): 用在局部变量上.
	
3、@Retention:
	(1)作用:
		(1)它规定了注解会存在于哪里。
		(2)它的可选值也被定义在java.lang.annotaion.RetentionPolicy枚举中。
	
	(2)使用:	
			@Retention(RetentionPolicy.SOURCE):
				注解只存在于“源码中”。不会在Class文件中出现。给编译器看的。
				
			@Retention(RetentionPolicy.CLASS):
				注解会存在于源码、class中。当JVM运行此类时,不会将注解信息读取到内存。给编译器看的。
				
			@Retention(RetentionPolicy.RUNTIME):
            	注解会存在于源码、class、内存中。给运行时的“注解解析器”看的。像JUnit的@Test。

3、注解解析器

1、介绍:
	就是通过Java技术获取注解数据的过程则叫做注解解析。

2、注解解析的相关接口;
	(1)Anontation:所有注解类型的公共接口,类似所有类的父类是Object。
	(1)AnnotatedElement:定义了与注解解析相关的方法。
		常用方法:
			isAnnotationPresent(): 判断当前对象上是否有指定注解,有true,没有false 
			getAnnotation();  获得当前对象上指定的注解对象。
			getAnnotations(); 获得当前对象及其从父类上继承的所有的注解对象。
			
3、注解解析器步骤:
	 //1.反射获取使用了注解的那个类的Class对象。
	 //2.创建对象。
	 //3.获取它内部的所有的成员方法。
	 //4.遍历每个方法。
	 //5.判断此方法上是否使用的自定义注解。
	 //6.有就执行此方法。

十一、JUnit单元测试

1、概念

1、作用:
	(1)单元测试”是指可以将“一部分” 代码进行单独测试。不用执行所有的代码。
	(2)JUnit是一个第三方公司实现的一个“单元测试”的工具包,它是基于“注解”的。

2、常用注解

1、@Test:测试方法。(可以定义多个)
		
2、@Before:在@Test方法之前运行。(可以定义多个)
	
3、@After : 在@Test方法之后运行。(可以定义多个)
	注意:
		测试方法,必须是公有、无参、无返回值,非静态的方法。
		
		
4、@BeforeClass :  用于测试静态方法。在所有的注解之前只运行一次。
5、@AfterClass  : 用于测试静态方法。在所有的注解之后只运行一次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值