Java一览和MySQL总结

Java篇

数据类型:

本质 就是规定占用内存空间的大小,用位和字节表示

引用数据类型
类,数组,接口
基本数据类型
数值型
整数型
byte,short,int,long
浮点型
float,double
布尔型
boolean 
字符型
char
Bit : 比特,1bit就是一个电子位,统称位
Byte : 字节 , 1字节= 8bit 8位
Short : 短整型 , 1short= 16bit
Int : 整型 ,1int = 32bit
Long : 长整型 , 1long=64bit
Float : 单浮点 32位
Double : 双浮点 64位
Boolean : 布尔型 ,4位 0001 true, 0000 false
Char : 字符 , 16位 , 

ASCII 码是字符和整型直接的一个映射关系
0 : 48
1 : 49
A : 65
B : 66
a : 97
c : 98


变量

:常量/字面量/直接量

  1. 常量:在程序整个声明周期不可变
  2. 字面量:直接量如System.out.prtinln(123);
  3. 变量:有名字的内存空间,可以找到,更改,访问。可以重复使用(强制 : 大小写字母,数字,下划线,美元符号,数字不能开头,不能使用关键字和保留字
    非强制 : 驼峰命名法,望文知文件名,变量名,方法名,类名,包名(文件夹名) 一切需要我们命名的地方,都要符合这个规则)

流程控制、循环、方法

流程控制

  1. 顺序结构
    正常编码,从上往下,从左到右执行即可
    在这里插入图片描述
    2.分支结构
    根据业务逻辑,某些代码在某种情况下执行
    或者是根据不同的情况执行不同的代码
    技术栈 : if…else … switch
    在这里插入图片描述

递归

学好递归要了解程序的运行机制
1 java程序编写
文本编辑器
2 java程序编译
Javac 命令
3 Java程序的执行
Java 命令
3.1 开启java虚拟机,载入对应的class文件,载入到静态区
3.2 jvm自动调用main方法
3.3 main被调用,JVM会在栈内存开辟一个main的空间,用于执行main中的代码
如果main中没有其他方法调用,执行完就弹栈,结束,JVM关闭
如果main方法中有其他方法调用,就会在栈内存再次开辟栈帧,把对应的方法放入栈内存,开始执行
如果被调用方法中还有其他方法调用,同上
一直到某一个方法栈中,没有其他方法调用,该方法如果代码执行完成,则该栈帧弹栈,如果需要返回数据,会占用临时空间把数据传递回去
弹栈后 返回上一个栈帧中方法调用处,继续执行
一直到main方法弹栈,则整个栈帧调用链结束.JVM关闭

栈内存是真正执行程序的地方,其他内存都是存储数据
方法调用 就等于是 压栈操作
方法执行结束 就等于是 弹栈操作

动态加载和静态加载
动态加载 : 只载入当前程序和需要的文件,如果执行过程中需要别的文件,再去硬盘中找
静态加载 : 开始执行,把所有和当前程序相关的文件,全部一次性载入

方法体内部对当前方法进行调用
在这里插入图片描述
常见问题
斐波那契数列:

package day_4;

/**
 * 声明一个方法,接收一个参数 判断传递的参数值再斐
 * 波那契数列的第几位上 如果不存在 打印 -1 , 如果存在 打印对应的位数
 * 
 * @author moon
 *
 */
public class Feb {
	public static void main(String[] args) {
		num(3);
		System.out.println(m(4));
	}
	//循环
	public static void num(int n) {
		if (n == 1 || n == 2) {
			System.out.println("1");
		}
		int i1 = 1;
		int i2 = 1;
		int i3 = 0;
		for (int i = 3; i <= n; i++) {
			i3 = i1 + i2;
			i1 = i2;
			i2 = i3;
		}
		System.out.println(i3);
	}
	//递归
	public static int m(int a) {
		if (a == 1 || a == 2) {
			return 1;
		}
		//m(a)=m(a - 1) + m(a - 2)
		return m(a - 1) + m(a - 2);
	}
}

实现代码:写一个递归方法,输入一个非负整数, 返回组成它的数字之和.

package day_4;

public class Recursion_4 {
	public static void main(String[] args) {
		// 实现代码:写一个递归方法,输入一个非负整数,
		// 返回组成它的数字之和.
		System.out.println(sum(437856621));
	}
	public static int sum(int a) {
		if (a < 9) {
			return a;
		} else {
			return a % 10 + sum(a / 10);
		}
	}
}

**

数组

**
经典排序
冒泡法

package test;

//
public class Test3 {

	public static void main(String[] args) {
		// 冒泡排序
		int[] a = { 91, 14, 6, 8, 923, 12 };
		System.out.println(m(a));
	}
//冒泡排序
	public static int m(int a[]) {
		for(int i=0;i<a.length-1;i++){
			for(int j=0;j<a.length-1-i;j++){
				if(a[j]>a[j+1]){
					int temp=a[j];
					a[j]=a[j+1];
					a[j+1]=temp;
				}
			}
		}
		return a[a.length-1];
	}
	//选择排序

}

二分法

package test;

public class Work6 {
	public static void main(String[] args) {
		// 给出一个有序数组
		// int a[] ={1,3,4,5,6,7,9,15,17,19,38,41,52,63,73,78};
		// 请编写二分查找,查询 9在第几位上,如果没有请返回-1
		int a[] = { 1, 3, 4, 5, 6, 7, 9, 12,15, 17, 19, 38, 41, 52, 63, 73, 78 };
		int result=search(a,78);
		System.out.println(result+1);
	}
public static int search(int[] a,int num){
	int start=0;
	int end=a.length-1;
	int m=(start+end)/2;
	while(start<=end){
		if(num==a[m]){
			return m;
		}else if(num>a[m]){
			start=m+1;
			//start开始查询后半段
		}else{
			end=m+1;
			//end开始查询前半段
		}
		m=(start+end)/2;
		
	}
	return -1;
}

}

选择排序

package work;
public class Work1 {
	public static void main(String[] args) {
		int[] a = { 11, 13, 4, 5, 12 };
		selectSort(a);
		for (int i = 0; i < a.length; i++) {
			System.out.println(a[i]);
		}
	}
	// 11,13,4,5,12 i = 0, min = 2
	// 13,4,11,5,12 i = 1, min = 0
	// 13,11,4,5,12 i = 2, min = 1
	// 13,11,5,4,12 i = 3, min = 2
	// 13,11,5,12,4 i = 4, min = 3
	// 13,11,5,12,4 i = 5, min = 3
	public static void selectSort(int[] a) {
		for (int i = 0; i < a.length; i++) {
			// 假设 第一个元素是最小的
			int min = i;
			// min+1 是因为 没有必要自己跟自己比较
			// j 不能从0开始,否则结果不对
			for (int j = i + 1; j < a.length; j++) {
				
				// 循环一次之后,x是最小元素的下标
				if (a[min] > a[j]) {
					min = j;
				}
			}
			// 判断 min 是否等于 i ,如果等于i 说明 i 就是最小的,如果不是 说明有比 i 还小的,就换位
			if (min != i) {
				int temp = a[min];
				a[min] = a[i];
				a[i] = temp;
			}
		}
	}
}

**

面向对象

**
面向对象和面向过程
面向过程 : 侧重分步骤
比如做饭 :
1 买食材
2 开火
3 加水…放米…
4 熟了
面向对象 : 侧重分类/模块
比如做饭 :
找个厨师
厨师.买食材
厨师.开火
厨师.加水…方米…

面向对象和面向过程都是解决问题的思路,或者是程序设计范型,只是侧重点不同
面向对象的基础也是面向过程,只是面向对象把具体的实现细节给封装了,让外界无法直接获取

优点 : 
易于维护、可扩展性、可重用性

方法有哪些,是什么
在这里插入图片描述
this、封装、继承
传值和传引用,
传引用,传递的是地址,如果地址传递到另一个地方,则拥有相同地址的变量,可以找到同一个对象,那么操作的数据也是相互的
在这里插入图片描述This,不能出现在静态上下文中
在这里插入图片描述
在这里插入图片描述链式调用
在这里插入图片描述
继承extends

Java中只支持单继承,不支持多继承,这样使继承关系比较简单,一个类 只有一个父类,易于程序管理,当然为了解决单继承功能变弱的问题,java又提出了接口,一个类可以实现N个接口
如果一个类,没有显示继承几个父类的话,则该类默认继承java.lang.Object
不管如何继承,最终任何类都直接/间接的成为Object的子类
Object是java提供的根类(祖宗)

抽象方法

package duotai;

public class Abstract {

	public static void main(String[] args) {
		
	}
}
abstract class A{
	public A(){
		
	}
	//没有方法体的成员方法,需要用abstract修饰
	public abstract void move1();
	public abstract void move2();
	public void m1(){
		System.out.println("父类的m1");
	}
}
abstract class C extends A{
	public abstract void move3();
}
class D extends C{
	@Override
	public void move3(){
		//
	}
	@Override
	public void move1(){
		
	}
	@Override
	public void move2(){
		
	}
}
class B extends A{
	@Override
	public void move1(){
	}
	@Override
	public void move2(){
		
	}
}
  • 1 懒汉模式
  • 2 饿汉模式
  • 实现步骤 :
  •  既然要控制创建对象的数量,就不能让用户去决定是否创建对象
    
  •  而创建对象的语句是 new 构造方法() ;  , 通过构造方法创建对象,
    
  • 而我们不让用户创建对象
  •  所以 应该不让用户访问构造方法,应该把构造方法私有化,
    
  • 这样创建对象的决定权就在当前类中
  • 1 构造方法私有化
  •  	上面步骤,已经把构造方法私有化了,就意味着用户创建不了对象了
    
  •  	那么这个时候,我们还需要想办法把该对象返回给用户,
    
  • 也就是必须提供一个专门获取这
    对象的方法
  •  	这个方法,一定是公共的静态方法
    
  • 2 创建一个公共的静态方法,用户获取当前类的对象,并且保证只创建一次
    *获取对象的方法有了,下面就是要保证对象的唯一性了,想要重复使用(唯一) 必须先存储
    *存储方式 : 需要创建一个当前类类型的变量
    *局部 变量 : 不行,因为每次调用该方法的时候,该变量都会重新初始化赋值
    *静态 变量 : 没问题,因为静态在整个程序生命周期中只初始化一次
    *成员 变量 : 不行,因为静态上下文中无法引用非静态属性
  • 3 创建一个私有化静态变量,用来存储当前类的对象
* 下面程序 : 类加载阶段创建对象 叫饿汉模式
public class Sing1 {
	//创建一个私有化静态变量,存储当前类的对象
	private static Sing1 obj=new Sing1();
	//构造方法私有化
	private Sing1(){
		System.out.println("Sing1");
	}
	//创建一个公共的静态方法,用户获取当前类的对象
	public static Sing1 getInstance(){
		return obj;
	}
}


 * 懒汉模式 : 第一次使用的时候,再创建对象
 */
public class Sing2 {
	private static Sing2 obj=null;
	private Sing2(){
		System.out.println("Sing2");
	}
	public static Sing2 getInstance(){
		if(obj==null){
			obj=new Sing2();
		}
		return obj;
	}
}

**

常用API

**
String:

  • String 是字符串类 , 在 java.lang.String , 所以使用不需要导包
  • 底层就是一个char数组,所以字符串很多特性就是数组特性
  • 数组特性 : 查询更改快,添加删除慢, 长度一旦确定不可更改
  •  字符串一旦确定 不能更改
    

为了提升字符串的效率,java提供了一个"缓存机制",字符串常量池

	//char数组转字符串
	char[] chars={'a','c','c','d','e'};
	String s5=new String(chars);
			//截取部分
	String s6=new String(chars,3,2);
	System.out.println(s6);
//获取字符串某个位置的字
String s1="asdfgh";
char c1=s1.charAt(2);
System.out.println(c1);
package _01String;

public class String4 {
public static void main(String[] args){
	//获取字符串某个位置的字
	String s1="asdfgh";
	char c1=s1.charAt(2);
	System.out.println(c1);
	
	//返回长度
	int l=s1.length();
	System.out.println(l);
	
	//判断字符串是否以指定字符串结束
	//是否以指定字符开始
	System.out.println("asddfdsf".endsWith("f"));
	System.out.println("afsdfadsf".startsWith("afs"));
	
	//比较两个字符串是否相等
	System.out.println("ads".equals("Ads"));
	System.out.println("ads".equalsIgnoreCase("Ads"));
	
	//把字符串转换为字节数组
	byte[] bytes="Abc".getBytes();
	for(byte a:bytes){
		System.out.println(a);
	}
	
	//获取指定字符串的起始索引下标,找不到返回 -1
	System.out.println("adfdfsadfbc".indexOf("d"));
	//获取最后一次出现的索引,找不到返回 -1
	System.out.println("adfdfsadfbc".lastIndexOf("d"));
	//在指定位置开始找,第一次出现的索引,找不到返回-1
	System.out.println("asfuox".indexOf("o", 4));
	System.out.println("asfuox".lastIndexOf("o", 5));
	
	// 把符合条件的字符用指定字符替换(不支持正则表达式)
	System.out.println("ffdagdgdffdsae".replace("f", "."));
	// 把符合条件的字符用指定字符替换(支持正则表达式)
	System.out.println("fFfdagdgdffdsae".replaceAll("f", "."));
	
	// 分割字符串,需要指定分隔符,支持正则表达式
	String time="13331,111,22";
	String[] s=time.split(",");
	for(String a:s){
		System.out.println(a);
	}
	
	//获取该字符串的子字符串(下标是起始位置,包含)
	System.out.println("afdadf".substring(2));
	// 截取字符串,开始位置(包含) 结束位置(不包含)
	System.out.println("afdadf".subSequence(1, 4));
	
	// 把字符串转换为char数组
	char[] chars="acva".toCharArray();
	for(char c:chars){
		System.out.println(c);
	}
	//转大写和小写
	System.out.println("asdf".toUpperCase());
	System.out.println("asdf".toUpperCase().toLowerCase());
	//两边去空格
	System.out.println("   x sdffsd   ".trim());
	
	// 调用对象的toString方法,并解决空指针异常
	Object obj=null;
	//obj.toString();
	System.out.println(obj);
}
}

操作字符串的类有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

package _01String;
/* java.lang.String
 * java.lang.StringBuffer
 * java.lang.StringBuilder 
 * 1 String 是不可变的字符串,一旦确定 长度不能更改,所以不适合做拼接操作
 * 2 StringBuffer和StringBuilder 是可变的字符串缓冲区,适合做拼接操作
 * 原理 : 预先在内存中申请一块空间,用来容纳更多的字符(字符数组)
 *	如果预留空间不够 会自动扩容,默认容量是16个字符
 * 
 * 3 StringBuffer和StringBuilder的区别
 *StringBuffer 线程安全,多线程环境下,没有问题,所以经常用于类中
 *StringBuilder 非线程安全,多线程环境下,可能有问题,所以经常用于方法中
 * 
 */
public class String6 {

	public static void main(String[] args) {
		//创建对象
		StringBuilder sb=new StringBuilder();
		//添加
		sb.append("a");
		sb.append("b");
		//插入指定位置
		sb.insert(2, "c");
		System.out.println(sb.length());
		System.out.println(sb);
		//返回当前的容量value.length。默认是16,
		//容量是新插入字符的可用存储量,否则将进行分配。
		System.out.println(sb.capacity());
		//转换为字符串
		String s=sb.toString();
		System.out.println(s);
		// 反转
		System.out.println(sb.reverse());
	}
}

package _01String;

public class String8 {
	public static void main(String[] args) {
		//创建对象
		StringBuilder sb=new StringBuilder();
		//添加
		sb.append("d");
		sb.append("e");
		//插入指定位置
		sb.insert(2, "f");
		System.out.println(sb.length());
		System.out.println(sb);
		System.out.println(sb.capacity());
		//转换为字符串
		String s=sb.toString();
		System.out.println(s);
		// 反转
		System.out.println(sb.reverse());
	}
}

date

package _03Data;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Date3 {

	public static void main(String[] args) {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
		Date date = new Date();
		String time = sdf.format(date);
		String[] times = time.split(":");
		int h = Integer.parseInt(times[0]);
		int m = Integer.parseInt(times[1])-10;
		if (m < 0) {
			h--;
			m += 60;		
		}
		System.out.println(h + ":" + m);

		// 获取当前时间毫秒数
		long currentTimeMillis = System.currentTimeMillis();
		// 减去10分钟的毫秒数
		currentTimeMillis = currentTimeMillis - 1000 * 60 * 10;
		// 创建时间对象
		SimpleDateFormat dds=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");
		Date t=new Date(currentTimeMillis);
		String str=dds.format(t);
		System.out.println(str);
	}
}

常见的数据结构及特征

ArrayList:既然是一串连续的存储结构,所以方便查找。新增和删除操作的时候,是要有移动位置的操作。所以ArrayList适用于存储,查询操作比较频繁的数据存储

package Test;

import java.util.ArrayList;

/*有序,可重复
		有指定下标,添加顺序和取出顺序一致
		ArrayList : 底层是个Object[] 数组,随机查询效率高,随机删除效率低,默认初始化时10,扩大之后是原来的1.5倍,并且是第一次添加数据的时候进行默认长度设置,只new的时候,不添加数据,则长度为0, 等于是 Object[]  elementData= {};  长度为0
		LinkedList : 底层是双向链表,随机查询效率低,随机删除效率高
		Vector : 已经过时,属于线程安全,而ArrayList是Vector的升级版 , 默认初始化是10,扩大之后是原来的2倍
 */
public class Collection_03_Arraylist {

	public static void main(String[] args) {
		ArrayList list = new ArrayList();
		// 尾部添加
		list.add(1);
		list.add(2);
		list.add(5);
		// 添加到指定位置
		list.add(0, 8);
		// 更改指定元素的值
		list.set(1, 11);
		// get : 根据索引 获取值
		System.out.println(list.get(1));
		// 个数
		System.out.println(list.size());
		// 是否包含(调用equals)
		System.out.println(list.contains(2));
		System.out.println(list);
		// 注意 : remove有方法重载,一个int (要删除的索引)  一个 object(要删除的数据)
		// 这个2 是删除的索引,并不是要删除2这个元素
		list.remove(1);
		// 这样才是删除2这个数据
		list.remove(new Integer(2));
		System.out.println(list);
	}

}

LinkedList:LinkedList底层使用的是双向循环链表数据结构,不适合存储需要大量查询操作的数据存储,插入就比ArrayList方便,不需要进行换位操作。

package Test;

import java.util.LinkedList;

/*队列 : 先到先得  , 栈 : 先进后出
 * 
 * 链表 : 链表中保存节点,而一个节点有三部分 1 添加的数据 2 上一个节点 3 下一个节点
 * 	链表是没有下标的,只能从头一个个找,所以查找慢
 * 	由于链表中 都是引用指向,所以删除快, 比如 3个元素 分别是 1,2,3  要删除 2  , 
 * 		只需要让 1 的下一个 = 1 的下一个的下一个 然后 3 的上一个 = 1的引用 
 * 
 * LinkedList是模拟的双向链表,就是  1 可以找到 2  , 2 也能找到 1
 * 
 * 添加指定位置 / 获取指定位置的值 : 可以传入索引,但是其实不是下标,而是LinkedList封装的方法,帮我们自动循环去找
 * 			本质 还是循环,因为链表是非连续的空间,只能从头一个一个找,
 * 			只不过LinkedList模拟了下标访问的方式,对我们使用起来,提供了便捷
 */
public class Collection_05LinkedList {

	public static void main(String[] args) {
		LinkedList linkedList = new LinkedList();
		// 尾部添加 true
		linkedList.add(1);
		// 尾部添加 void
		linkedList.addLast(2);
		// 插入指定位置
		linkedList.add(0,2);
		// 首部添加 void
		linkedList.addFirst(2);
		// 首部添加 void
		linkedList.push(3);
		// 尾部添加 true
		linkedList.offer(22);
		// 首部添加 true
		linkedList.offerFirst(1);
		// 尾部添加 true
		linkedList.offerLast(4);
		
		// 本质就是在调用两个方法 : linkLast 和 linkFirst
		// 根据下标获取
		System.out.println(linkedList.get(0));
		System.out.println(linkedList.get(0));
		// 获取首元素
		System.out.println(linkedList.getFirst());
		// 获取尾元素
		System.out.println(linkedList.getLast());
		// 删除第一个元素,并返回该元素
		linkedList.pop();
		// 删除最后一个元素,并返回该元素
		linkedList.poll();
		// remove重载  int是根据索引删除, Object 是根据内容删除
		linkedList.remove(1);
		linkedList.remove(  new Integer(22) );
	}

}

TreeSet:底层数据结构是二叉树有序的,并且没有重复元素。可以指定一个顺序。

package Test_01;

import java.util.Set;
import java.util.TreeSet;

/**16
 * 1 要添加的原始 实现 java.lang.Comparable接口 并实现compareTo方法
 * 2 写一个比较器类,实现java.util.Comparator比较器接口
 * 
 * 很多常用的类中都实现了Comparable接口 并实现compareTo方法
 * 	比如 Integer , String , Date等
 * 
 * 所以我们自定义类型的时候,一定要弄比较器类 
 */
public class TreeSet_02 {

	public static void main(String[] args) {
		Set set=new TreeSet();
		set.add(new User(19,"saf"));
		//重复就不会添加
		set.add(new User(29,"saf"));
		set.add(new User(19,"saff"));
		set.add(new User(14,"sf"));
		System.out.println(set);
	}
}

class User implements Comparable {
	int age;
	String name;

	public User(int age, String name) {
		super();
		this.age = age;
		this.name = name;
	}
	@Override
	public String toString() {
		return "User [age=" + age + ", name=" + name + "]";
	}

	@Override
	public int compareTo(Object o) {
		//this是要添加的元素
		//o是集合中的每一个元素
		//返回值是0说明重复,不添加
		//返回值大于0的值,说明要添加的这个元素比集合中的元素大,往后放
		//返回小于0的值,说明要添加的元素比集合中的元素小,往后放
		if(o instanceof User){
			User user=(User)o;
			int result=age-user.age;
			if(result==0){
				if(name.equals(user.name)){
					return 0;
				}else{
					return 1;
				}
			}
			return result;
		}
		return 0;
	}
}

HashSet:链表和红黑树,元素没有顺序(底层用的是HashMap,HashMap本身中的元素度没有顺序)、元素不能重复。

HashMap:链表和红黑树,Null可以做主键,但只能有一个,可以有多个Value为Null。适用于在Map中插入、删除和定位元素。

package Test_01;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Map_02 {
	/**
	 * 17 map和集合虽然没有关系,但是操作几乎是一样的
	 * 
	 * 添加 : Object put(Object key , Object value); void clear() : 清空map int
	 * size() boolean isEmpty()
	 * 
	 * 
	 * Object get(Object key) : 根据key 获取value Collection values() :
	 * 获取所有的value,返回集合 boolean containsKey(Object key) : 判断是否包含某个key boolean
	 * containsVallue(Object value) : 判断是否包含某个value Set keySet() :
	 * 把map中所有的key取出,返回set Set entrySet() : 把键值对保存entry中,以set形式返回 V
	 * remove(Object key) : 根据key删除映射,返回value值
	 * 
	 * map不能直接遍历
	 * 
	 */
	public static void main(String[] args) {
		Map<String,Integer> map=new HashMap<String,Integer>();
		map.put("a", 1);
		map.put("b", 2);
		map.put("c", 7);
		map.put("d", 4);
		map.put("e", 5);
		System.out.println(map);
		//keySet
		Set<String> keys=map.keySet();
		for(String key:keys){
			Integer value=map.get(key);
			System.out.println(key+":"+value);
		}
		
		//entrySet
		Set<Entry<String,Integer>>entrys=map.entrySet();
		for(Entry<String,Integer>entry:entrys){
			String key=entry.getKey();
			Integer value=entry.getValue();
			System.out.println(key+":"+value);
		}
	}
}

TreeMap:有序的,适用于按自然顺序或自定义顺序遍历键(key)。

LinkedHashMap:有序、Key和Value都允许空、Key重复会覆盖、Value允许重复

Vector:Vector是线程安全的,可以由多个线程访问一个Vector对象。但当一个线程访问的话,保证线程安全会消耗一定的资源。一个线程访问就无需考虑是否线程安全的问题,使用ArrayLis.

Map转list 并以value排序

package Test_01;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

	public class MapToList {
		public static void main(String[] args) {
			Map<String, Integer> map = new HashMap<String, Integer>();
			map.put("aa", 3);
			map.put("bb", 2);
			map.put("dd", 1);
			map.put("cc", 12);
			
			// 转换为set
			Set<Entry<String, Integer>> set = map.entrySet();
			//转换为list
			List <Entry<String, Integer>> list = new ArrayList<Map.Entry<String,Integer>>(set);
			//排序
			Collections.sort(list,new Comparator<Entry<String, Integer>>() {

				@Override
				public int compare(Entry<String, Integer> o1,
						Entry<String, Integer> o2) {
					return o1.getValue() - o2.getValue();
				}
			});
			// 遍历
			for (Entry<String, Integer> entry : list) {
				System.out.println(entry);
			}
		}
	}



**

IO流

**
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

文件的复制

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;


public class _05Cooy_all {
/**19
 * 1 文件复制 : 本质就是输入和输出
 * 				1 完成文件输入
 * 				2 完成文件输出
 * 				3 把输入读取到的数据,通过输出流输出(把打印到控制台,换成输出流的写出即可)
 * 		2 获取指定目录下所有子目录
 * 				1 获取目录对象
 * 				2 获取该目录的直接子目录
 * 				3 获取该目录的所有后代目录(遍历子目的,得到孙目录,在遍历.....)
 * 		3 整合所有后代目录和文件复制
 * 				1 获取到所有后代目录的文件对象
 * 				2 通过文件对象获取文件的全路径
 * 				3 判断目标全路径目录是否存在,不存在就创建
 * 				4 通过全路径就可以创建输入流和输出流
 * 				5 输出
 */
	public static void main(String[] args) {
		//被复制目标路径
		File file = new File("E:\\0");
		checkMenu(file);
		System.out.println("完成");
	}
	public static void checkMenu(File f){
		// 判断是否是文件
		if (f.isFile()) {
			// 获得全路径
			String filePath = f.getAbsolutePath();
			// 设置新路径  设置关键是substring
			String newFilePath = "E:/copy"+filePath.substring(2);
			// 判断目标目录是否存在
			File parentFile = new File(newFilePath).getParentFile();
			if (!parentFile.exists()) {
				// 不存在就递归创建
				parentFile.mkdirs();
			}
			// 到这里 目标目录 一定存在
			try (
					FileInputStream fis = new FileInputStream(filePath);
					FileOutputStream fos = new FileOutputStream(newFilePath);
					BufferedInputStream bis = new BufferedInputStream(fis);
					BufferedOutputStream bos = new BufferedOutputStream(fos);
					){
				byte[] bytes = new byte[10240000];
				int tmp = 0;
				while ((tmp = bis.read(bytes)) != -1) {
					bos.write(bytes, 0, tmp);
				}
				bos.flush();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}
		// 不是文件就获取它所有的子文件,再把所有的子文件递归传入
		File[] fs = f.listFiles();
		for (File file : fs) {
			checkMenu(file);
		}
	}
}

代码优化 降低低耦合

package Work_01;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 19 多文件内容合并读问题: 任意给定多个文本文件路径合并为一个大的文本文件, 并保存名为merge.txt。
 * 1)、如给定"one.txt”,“two.txt”,“three.txt"三个文本文件路径
 * 2)、内部分别存储内容"内容One"、“内容Two”、“内容Three"
 * 3)、合并完成后merge.txt的结果为三行文字,分别为“内容One"、"内容Two"、"内容Three"
 */
public class IO_MergeThree {

	public static void main(String[] args) throws IOException {
		// 写入内容
		FileWriter fw = new FileWriter("E:/a/one.txt");
		fw.write("内容One");
		fw.flush();
		FileWriter fw2 = new FileWriter("E:/b/two.txt");
		fw2.write("内容Two");
		fw2.flush();
		FileWriter fw3 = new FileWriter("E:/c/three.txt");
		fw3.write("内容Three");
		fw3.flush();
		// 写出merge
		FileWriter os = new FileWriter("E:/merge.txt");
		// 添加缓冲流
		BufferedReader bs1 = new BufferedReader(new FileReader("E:/a/one.txt"));
		BufferedReader bs2 = new BufferedReader(new FileReader("E:/b/two.txt"));
		BufferedReader bs3 = new BufferedReader(new FileReader("E:/c/three.txt"));
		BufferedWriter bo = new BufferedWriter(os);

		// 第一次输出
		char[] c0 = new char[1024];
		int tem0 = 0;
		while ((tem0 = bs1.read(c0)) != -1) {
			// 字节输出
			bo.write(new String(c0, 0, tem0));
		}
		bo.write("\n");
		bo.flush();

		// 第二次输出
		char[] c1 = new char[1024];
		int tem1 = 0;
		while ((tem1 = bs2.read(c1)) != -1) {
			// 字节输出
			bo.write(c1, 0, tem1);
		}
		bo.write("\n");
		bo.flush();

		// 第三次输出
		char[] c2 = new char[1024];
		int tem2 = 0;
		while ((tem2 = bs3.read(c2)) != -1) {
			// 字节输出
			bo.write(c2, 0, tem2);
		}
		bo.flush();
	}

}

package Work_01;
/**
 * 1 编码实现一个压缩包a.rar文件的复制。
 */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class IO_01_copy {

	public static void main(String[] args) throws IOException {
		//读取
		FileInputStream is = new FileInputStream("E:/work/a.rar");
		//写出
		FileOutputStream os = new FileOutputStream("E:/a.rar");
		//添加缓冲流
		BufferedInputStream bs=new BufferedInputStream(is);
		BufferedOutputStream bo=new BufferedOutputStream(os);
		byte[] bytes = new byte[10240];
		int tem = 0;
		while ((tem = bs.read(bytes)) != -1) {
			//字节输出
			bo.write(bytes, 0, tem);
		}
		bo.flush();
	}

}

package Work_01;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Set;
import java.util.TreeSet;

/**19
 * 文件input.txt中存放着10行数字,为0至6区间的整数,
 * 求10行数字去重后还有多少行?分别是哪些数字?排序序出。
 */
public class IO_list {

	public static void main(String[] args) throws IOException {
		//添加缓冲流
		BufferedReader bi=new BufferedReader(new FileReader("E:/input.txt"));
		//添加Set比较
		TreeSet<Integer> li=new TreeSet<Integer>();
		String tem=null;
		//字符串存储读取
		byte[] bytes=new byte[10240];
		while((tem=bi.readLine())!=null){
			li.add(Integer.parseInt(tem));
		}	
		System.out.println("去重后剩余"+li.size()+"行");
		System.out.println(li);
	}

}

**

线程池

**
在这里插入图片描述在这里插入图片描述

package _01_Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 1 减少创建和销毁次数
 * 
 * 2 可以重复使用
 * 
 * 3 统一管理线程数量 
 */
public class _05_NewCachedThreadPool {

	public static void main(String[] args) {
				// 创建一个可缓存的线程池
				// 若没有可以回收的,就创建新的线程,线程池规模没有限制,数量不固定
				ExecutorService cachExecutorService = Executors.newCachedThreadPool();
				for (int i = 0; i < 10; i++) {
					// 循环创建,并且都在线程中睡眠一秒,会创建10个线程
					cachExecutorService.execute(new Runnable() {
						@Override
						public void run() {
							try {
								Thread.sleep(1000);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							System.out.println(Thread.currentThread().getName());
						}
					});
				}
				//此时上面10个线程都已执行完成,有线程空闲,不会新建线程
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 执行任务
				cachExecutorService.execute(new Runnable() {
					@Override
					public void run() {
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName());
					}
				});
				System.out.println("OOOO");
				// 关闭线程池
				cachExecutorService.shutdown();
	}
}

网络编程

概述
Java是 Internet 上的语言,它从语言级上提供了对网络应用程 序的支持,程序员能够很容易开发常见的网络应用程序。
Java提供的网络类库,可以实现无痛的网络连接,联网的底层 细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并 且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一 的网络编程环境

在这里插入图片描述

lambda表达式

基本使用

package test;

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
 * 23	
 * 通过Stream.generate : 是一个无限流(无限大),	
 * 再操作的时候,通过limit进行最大数量控制
 * 常用的转换算子 : filter,distinct,map,limit,skip,flatMap
 * 
 * filter : 对元素进行过滤筛选,不符合条件的不要
 * 
 * distinct : 去除重复
 * 
 * skip : 跳过多少个元素
 * 
 * limit : 取一个集合中的 前几个数据
 * 
 * map : 在集合的遍历中对数据进行操作,比如更改,公司所有员工薪水涨10%
 * 
 * flatMap : 解决字符串数组
 */
public class Stream_01 {
	public static void main(String[] args) {
		//数组 创建 Stream.of
		String[] strings = {"a","s","d","f","g","h"};
		Stream<String> stream1 = Stream.of(strings);
		
		//通过集合
		List<String>  strings2 = Arrays.asList(strings);
		Stream<String> stream2 = strings2.stream();
		
	
		// 参数 Supplier,有一个get方法,是无参有返回值
		// get方法的返回值 就作为集合中的数据,下面这中就等于都赋值为1
		Stream<Integer> generate  = Stream.generate(()->1);
		//使用limit 设置元素个数
		generate.limit(4).forEach(x->System.out.println(x));
		
		//Stream.Iterate 来创建
		// 是一个有序的无限流,所以建议使用limit进行最大数量控制
		// 第一个参数是数据的起始值,第二个参数是Function,所以是有参有返回值
		// 1 就是起始值是1 , x+2 步长为2 , 就类似于一个死循环,起始值是1,步长为2
		Stream<Integer> iterate = Stream.iterate(1, x->x+2);
		iterate.limit(3).forEach(x->System.out.println(x));
		
		//通过已有类API
		String str = "badsfasd";
		IntStream chars = str.chars();
		chars.forEach(x->System.out.println(x));
	}
}

应用

package test;

/**
 24 1. 找出2011年发生的所有交易,并按交易额排序(从低到高)
 2. 交易员都在哪些不同的城市工作过
 3. 查找所有来自剑桥的交易员,并按姓名排序
 4. 返回所有交易员的姓名字符串,按字母顺序排序
 5. 有没有交易员是在米兰工作的?
 6. 所有交易中,最高的交易额是多少
 7. 找到交易额最小的交易
 */
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Work_01 {
	public static List<Transaction> transactions = null;
	static {
		Trader raoul = new Trader("Raoul", "Cambridge");
		Trader mario = new Trader("Mario", "Mqilan");
		Trader alan = new Trader("Alan", "Cambridge");
		Trader brian = new Trader("Brian", "Cambridge");
		transactions = Arrays.asList(new Transaction(brian, 2011, 300),
				new Transaction(raoul, 2012, 1000), new Transaction(raoul,
						2011, 400), new Transaction(mario, 2012, 710),
				new Transaction(mario, 2012, 700), new Transaction(alan, 2012,
						950));
	}

	public static void main(String[] args) {
		// 1. 找出2011年发生的所有交易,并按交易额排序(从低到高)
		Stream<Transaction> stream1 = transactions.stream();
		stream1.filter(tran -> tran.getYear() == 2011)
				.sorted((x, y) -> x.getValue() - y.getValue())
				.forEach(x -> System.out.println(x));
		System.out.println("------------------------------");
		// 2. 交易员都在哪些不同的城市工作过
		stream1 = transactions.stream();
		stream1.map(x -> x.getTrader().getCity()).distinct()
				.collect(Collectors.toList())
				.forEach(x -> System.out.print(x + " "));
		System.out.println();
		System.out.println("---------------------------------");
		// 3. 查找所有来自剑桥的交易员,并按姓名排序
		stream1 = transactions.stream();
		List<Transaction> value1 = stream1.filter(
				x -> x.getTrader().getCity().equals("Cambridge")).collect(
				Collectors.toList());
		System.out.println(value1);
		System.out.println("---------------------------------");
		// 4. 返回所有交易员的姓名字符串,按字母顺序排序
		stream1 = transactions.stream();
		List<String> value2 = stream1.map(x -> x.getTrader().getName())
				.distinct().collect(Collectors.toList());
		value2.sort((x, y) -> x.compareTo(y));
		System.out.println(value2);
		System.out.println("---------------------------------");
		// 5. 有没有交易员是在米兰工作的?anyMatch
		stream1 = transactions.stream();
		if (stream1.anyMatch(x -> x.getTrader().getCity().equals("Milan"))) {
			System.out.println("有");
		} else {
			System.out.println("没有");
		}
		System.out.println("---------------------------------");
		// 6. 打印生活在剑桥的交易员的所有交易额
		stream1 = transactions.stream();
		List<Integer> value3 = stream1
				.filter(x -> x.getTrader().getCity().equals("Cambridge"))
				.map(x -> x.getValue()).collect(Collectors.toList());
		System.out.println(value3);
		System.out.println("---------------------------------");
		// 7. 所有交易中,最高的交易额是多少max
		Integer value4  = transactions.stream().map(x -> x.getValue())
				.max((x, y) -> Integer.compare(x, y)).get();
		System.out.println(value4);
		System.out.println("---------------------------------");
		// 8. 找到交易额最小的交易 min
		Optional<Transaction> value5 = transactions.stream().min(
				(x, y) -> x.getValue() - y.getValue());
		System.out.println(value5);
	}
}

// 商人
class Trader {
	private String name;
	private String city;

	public Trader() {
	}

	public Trader(String name, String city) {
		this.name = name;
		this.city = city;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

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

// 交易
class Transaction {
	private Trader trader;
	private int year;
	private int value;

	public Transaction() {
	}

	public Transaction(Trader trader, int year, int value) {
		this.trader = trader;
		this.year = year;
		this.value = value;
	}

	public Trader getTrader() {
		return trader;
	}

	public void setTrader(Trader trader) {
		this.trader = trader;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return "Transaction{" + "trader=" + trader + ", year=" + year
				+ ", value=" + value + '}';
	}
}

sql篇

sql综合测试

course表
在这里插入图片描述teacher表
在这里插入图片描述

sc表
在这里插入图片描述student表
在这里插入图片描述

学生表 Student
create table Student(Sid varchar(6), Sname varchar(10), Sage datetime, Ssex varchar(10));
insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
insert into Student values('02' , '钱电' , '1990-12-21' , '男');
insert into Student values('03' , '孙风' , '1990-05-20' , '男');
insert into Student values('04' , '李云' , '1990-08-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吴兰' , '1992-03-01' , '女');
insert into Student values('07' , '郑竹' , '1989-07-01' , '女');
insert into Student values('08' , '王菊' , '1990-01-20' , '女');

成绩表 SC

create table SC(Sid varchar(10), Cid varchar(10), score decimal(18,1));
insert into SC values('01' , '01' , 80);
insert into SC values('01' , '02' , 90);
insert into SC values('01' , '03' , 99);
insert into SC values('02' , '01' , 70);
insert into SC values('02' , '02' , 60);
insert into SC values('02' , '03' , 80);
insert into SC values('03' , '01' , 80);
insert into SC values('03' , '02' , 80);
insert into SC values('03' , '03' , 80);
insert into SC values('04' , '01' , 50);
insert into SC values('04' , '02' , 30);
insert into SC values('04' , '03' , 20);
insert into SC values('05' , '01' , 76);
insert into SC values('05' , '02' , 87);
insert into SC values('06' , '01' , 31);
insert into SC values('06' , '03' , 34);
insert into SC values('07' , '02' , 89);
insert into SC values('07' , '03' , 98);


课程表 Course

create table Course(Cid varchar(10),Cname varchar(10),Tid varchar(10));
insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');



教师表 Teacher

create table Teacher(Tid varchar(10),Tname varchar(10));
insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');

– 1查询在 SC 表存在成绩的学生信息

SELECT * from student s INNER JOIN (SELECT Sid from sc) c on s.Sid=c.Sid GROUP BY Sname;

– 2 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )

select * from student s left JOIN (SELECT Sid,COUNT(Sid) num,SUM(score) sum from sc GROUP BY Sid)c on s.Sid=c.Sid GROUP BY s.Sname;

– 3查有成绩的学生信息

select * from student s inner JOIN (SELECT Sid,COUNT(Sid) num,SUM(score) sum from sc GROUP BY Sid)c on s.Sid=c.Sid GROUP BY s.Sname;

– 4 查询「李」姓老师的数量

SELECT COUNT(*) from teacher WHERE Tname LIKE '李%';

– 5查询学过「张三」老师授课的同学的信息

SELECT * FROM student st INNER JOIN(SELECT Sid from sc s INNER JOIN (SELECT Tname,Tid from teacher) t on s.Cid=t.Tid WHERE t.Tname='张三')a on st.Sid=a.Sid;

– 6查询没有学全所有课程的同学的信息

select * from student s left JOIN (SELECT Sid,COUNT(Sid) num,SUM(score) sum from sc GROUP BY Sid)c on s.Sid=c.Sid GROUP BY s.Sname HAVING num<>3 or num is null;

– 7查询和” 01 “号的同学学习的课程完全相同的其他同学的信息

select * from student where sid in (select b.sid from sc b where b.sid 
not in (select a.sid from sc a where a.cid not in 
(select cid from sc where sid = '01')) and b.sid <> '01' group by b.sid 
having count(*)  = (select count(*) from sc where sid = '01'));

–8查询至少有一门课与学号为” 01 “的同学所学相同的同学的信息

select * from student where sid in (SELECT Sid from sc where Cid in (SELECT cid from sc WHERE Sid='01'));

– 9查询没学过”张三”老师讲授的任一门课程的学生姓名

SELECT * from student WHERE Sid not in(select Sid FROM sc where Cid='03' GROUP BY Sid);

– 10 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

SELECT Sname,s.Sid,s.ave from student st INNER JOIN(SELECT Sid,avg(score)ave from sc WHERE score<60 GROUP BY Sid HAVING COUNT(*)>=2)s on st.Sid=s.Sid;

– 11 检索” 01 “课程分数小于 60,按分数降序排列的学生信息

SELECT * from student where Sid in(SELECT Sid from sc where score<60 and Cid='01' ORDER  BY score desc);

– 12按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩

SELECT Sid,avg(score)ave from sc GROUP BY Sid ORDER BY ave desc;

SELECT Sid,GROUP_CONCAT(Cid,'=',score ORDER BY score desc SEPARATOR ' | ')s_score,avg(score) ave from sc
GROUP BY Sid;

– 13 查询各科成绩最高分、最低分和平均分

SELECT c.Cid,c.Cname,MAX(score),MIN(score),avg(score) from sc s 
INNER JOIN (SELECT Cid,Cname from course)c on s.Cid=c.Cid GROUP BY c.Cid;

– 14 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比

select s.Cid,C.Cname,
((select COUNT(*) from sc where Cid=c.Cid and score>=85 and score<=100)/(select COUNT(*)from sc where Cid=c.Cid)) '100-85',
((select COUNT(*) from sc where Cid=c.Cid and score>=70 and score<85)/(select COUNT(*)from sc where Cid=c.Cid)) '85-70',
((select COUNT(*) from sc where Cid=c.Cid and score>=60 and score<=70)/(select COUNT(*)from sc where Cid=c.Cid)) '70-60',
((select COUNT(*) from sc where Cid=c.Cid and score>=0 and score<=60)/(select COUNT(*)from sc WHERE Cid=c.Cid)) '60-0' 
from sc s INNER JOIN course c on s.Cid=c.Cid GROUP BY s.Cid

– 15 查询出只选修两门课程的学生学号和姓名

SELECT s.Sid,st.Sname from sc s INNER JOIN (SELECT Sid,Sname from student)st on s.Sid=st.Sid GROUP BY Sid HAVING COUNT(1)=2

–16 查询名字中含有「风」字的学生信息

SELECT * from student WHERE Sname like '%风%'

-17 查询 1990 年出生的学生名单

SELECT * from student WHERE Sage like '1990%'

– 18 成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

SELECT DISTINCT *,MAX(score) FROM student st INNER JOIN(SELECT Sid,score from sc s INNER JOIN (SELECT Tname,Tid from teacher) t on s.Cid=t.Tid WHERE t.Tname='张三')a on st.Sid=a.Sid;

– 19查询各学生的年龄,只按年份来算

select *,round(datediff(CURRENT_DATE(),sage)/365) as age  from student

– 20按照出生日期来算,当前月日 < 出生年月的月日则,年龄减一

SELECT *,TIMESTAMPDIFF(year,s.sage,CURRENT_DATE()) AS age FROM student s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值