javaSE系统复习

CAS(compare and swap)

CPU拿一个旧地址去找一个需要修改的数据,如果地址一致,则说明这个数值没有被修改过,则cpu将其替换成新值;如果对比地址不一致,则说明数据已经被别的线程修改,放弃已经所做的操作,然后重新执行刚才的操作。

Lambda

方法引用:方法引用出现的目的是为了解决所需的操作已经存在的情况。

当我们需要传递的操作已经存在,那就不必再费尽心思的再写一个出来啦,直接使用方法引用来将已有的方法给它就行了。

方法引用使用“::”双英文冒号组成的操作符来指定方法。

使用方法引用之后,你会很不适应,因为参数哪去啦???

是的,参数不再是显式传递,采用方法引用之后,参数会自动传递,我们举个例子看看简单的原理。

public class LanbdaTest {
    public static String getName(Supplier<String> supplier){
        return supplier.get();
    }
    public static void main(String[] args) {
        Person person = new Person("huahua");
        System.out.println(getName(person::getName));
    }
}
class Person{
    private String name;
    public Person(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

解析:
首先我们使用了方法引用:person::getName,Person类中有已定义好的获取name的方法,这里就可以直接引用该方法。Supplier是供应者,可以无中生有,也就是不需要参数,产生一个返回值。

Person中的getName方法,明显就符合Supplier的格式,没有参数,但是返回了一个结果,所以这里就可以直接传递person::getName。

方法引用的种类:

  • 类的构造器引用:ArrayList::new、String[]::new
  • 类的静态方法引用:String::valueOf、Integer::valueOf
  • 类的实例方法引用:String::length、Person::getName
  • 对象的实例方法引用:sring::length、person::getName

方法引用于Lambda可以算是平等,并列的关系,Lambda用于自定义操作,方法引用用于引用已存在的操作。

//1.文件目录
        File fileDir=new File("D:/resource");
        //2.筛选
        File [] files=fileDir.listFiles((f)->!f.isDirectory()&&f.getName().endsWith(".js"));

BigDecimal

用来对超过16位有效位的数进行精确的运算

对于常用的加,减,乘,除,BigDecimal类提供了相应的成员方法。

​ public BigDecimal add(BigDecimal value); //加法
  public BigDecimal subtract(BigDecimal value); //减法
  public BigDecimal multiply(BigDecimal value); //乘法
  public BigDecimal divide(BigDecimal value); //除法

(random/length)

1.Math.random

Math.random()*10 输出随机变量方法,使用:"Math.random()*****数量"

​ 如:(int)(Math.random()*10); //随机取一个10以内的整数

​ 例如:定义一个随机1到5(取不到5)的变量 [1,5)

​ Math.random()*(n-m)+m,生成大于等于m小于n的随机数;

​ int number=(int)(Math.random()(5-1)+1);
​ int number = (int)(Math.random()
4+1);取值正好是[1,5)

2.length

length() 方法用于返回字符串的长度。

//例
public class Test {
    public static void main(String args[]) {
        String Str1 = new String("www.runoob.com");
        String Str2 = new String("runoob" );

        System.out.print("字符串 Str1 长度 :");
        System.out.println(Str1.length());
        System.out.print("字符串 Str2 长度 :");
        System.out.println(Str2.length());
    }
}
//随机输入一个100以内数组
int[] arr=new int[10];
for(int i=0;i<arr.length;i++){
    int value= (int)Math.random()*100;
    arr[i]=value;
}
System.out.println(Arrays.toString(arr));

(Scanner/if/equals)

1.equals

使用方法 ''变量1.equals(变量2)” //变量一定要保证有一个具体的数据,排除null值

2.Math.pow

Math.pow(数字,几次幂) 使用:sum=Math.pow(3,2);

3.Math.PI=3.14

4.Sanner

Sanner input = new Scanner(System.in); //需要导包,程序开头加 "import java.util."😭可以换成Scanner)

​ *****使用:int 变量 = input.nextInt(); //读一个int netxDouble/Float/Byte/Short() 只能读取空格之前的内容(空格后的数据会保留传给下一个变量),光标依然在10的后面
​ String 变量 = input.nextLine(); //读一个字符串
​ String 变量 = input.next(); //只能读取空格之前的内容(空格后的数据会保留传给下一个变量)

​ *****如果使用netxLine之前有netxDouble/Float/Byte/Short/next(),nextLine只能读到一个换行,不让用户录入数据,使用netLine()或者添加一行"input.nextIine();"解决

5.boolean嵌套

合理使用boolean嵌套,优化程序

boolean flag = (chinese>90 && history>80)||(chinese==100 && history>70);

6.debug部分快捷键

debug调试:双击添加断点,F6:逐行运行程序,F5:进入底层代码,F8:运行到下一个断点

*input.close()//关闭该变量,会关闭该程序所有的该变量

※使用while和if实现登陆

while("n".equals(answer)) {
			System.out.println("输入账号");
			answer = input.nextLine();
		}
if("y".equals(answer)) {
			System.out.println("********");
		}

卫语句

例:

if (true){
            if (true){
                if (true){
                    for (){
                        if (true){
                            业务代码
                        }
                    }
                }
            }
        }

在有比较复杂的判断和需要遍历处理业务时候,经常会出现上面这种情况,这些代码在执行起来是没问题。但是这样的代码是一次性代码,过一段时间,可能自己都不敢动这里的逻辑了,更何况下一个接盘侠呢。
这样的代码很难弄明白每个条件的作用和执行的流程,当你读到最里面一层的时候,估计你已经记不起来当初是为什么走进这个if的了,对代码的执行流程完全不可控了。因为执行你预期的流程并不明显,为了简化这种情况,需要将特殊情况隔离到立即结束执行的单独条件中。
这样就能把箭头代码给拉平.

if (false){

        }
        if (false){

        }
        if (false){

        }
        for (){
            if (false){
                continue;
            }
            业务代码
        }

这是其中一类,可以将失败前置。只要有一个条件不通过,就快速返回失败,如果到了最后一行,说明所有判断都通过,剩下的就是你预期的结果。而不是一直查成功。

if(obj != null){
  doSomething();
}
 
转换成卫语句以后的代码如下:
if(obj == null){
   return;
}
doSomething();

(排序/数组)

split

public String[] split(String regex)  //用指定的字符分割字符串数组,要用正则表达式转义如:"\\,""\\|"
例如,字符串"boo:and:foo"使用以下表达式得到以下结果: 
Regex Result 
: { "boo", "and", "foo" } 
o { "b", "", ":and:f" } 

1.数组

使用:

​ 初始化: 数据类型[] 变量名 = new 数据类型[length]

​ int[] array = new int[3]; // 空间大小为3的array数组

​ int[] array = {a,b,c,d,e};

2.Arrays

2.1Arrays.toString

​ Arrays:将数组元素转换成字符串进行打印(需要导包)

​ 使用:Arrays.toString(数组名)

​ 数组.length:获得数组长度

​ 二维数组不能直接使用,需要Arrays.deepToString(二维数组[]);

2.2Arrays.copyOf

​ 复制数组元素到新数组

​ int[] newArray = Arrays.copyOf(源数组,新数组的长度);

2.3Array.equals

​ 比较数组元素是否一致

​ 例:System.out.println(Arrays.equals(arr1,arr2));

​ *数组间不能以"=="或者"equals"直接比较,这样比较的是数组的地址

5.增强for循环

​ foreach:for(数组元素数据类型 变量:数组变量名称)

​ 将数组里面的每个元素赋值给变量,不能索引具体元素

6.冒泡法

for(int i=1;len = arrlength;i<len;i++){
    for(int j=0;j<len-i;j++){
        //相邻元素互相比较
        if(arr[j]>arr[j+1]){
            //交换位置
            int temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
        }
    }
}

7.选择排序

for(int i=0,len = arr.length;i<;en-1;i++){
    int min = arr[i];
    int minIndex = i;
    for(int j=i+1;j<len;j++){
        if(min>arr[j]){
            min = arr[j];
            minIndex = j;
        }
    }
}

8.插入排序(升/降)

for(int i=1,len = arr.length;i<len;i++){
     int temp = arr[i];
     int leftIndex = i-1;
     while(leftIndex>=0 && arr[leftIndex]>temp){        
         arr[leftIndex+1] = arr[leftIndex];//左边元素要移动,大数向右
         leftIndex--;
     }
    arr[leftIndex+1]  = temp;
 }
倒叙
for(int i=1;i<arr1.length;i++) {
	int space = arr1[i];//  取第二个数
	int left = i-1;//=指向第一个数
	while(left>=0 && arr1[left]<space) {
		arr1[left+1] = arr1[left];// 小数向右移
		left--;// 找回指向空格的索引
	}
	arr1[left+1] = space; //填空
}

9.Arrays.sort(数组变量); //升序

使用:

	int[] a = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
    Arrays.sort(a);

9.1Arrays.sort区间排序

​ Arrays.sort(int[] a, int fromIndex, int toIndex)

​ 这种形式是对数组部分排序,也就是对数组a的下标从fromIndex到 toIndex-1的元素排序,注意:下标为toIndex的元素不参与排序。

int[] a = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
          Arrays.sort(a, 0, 3);
          for(int i = 0; i < a.length; i ++) {
              System.out.print(a[i] + " ");

(方法)

1.方法

​ 1.1访问权限修饰符:public/private/protectde/默认

​ 1.2普通修饰符(关键字) : static/final/abstract

​ 1.3返回值类型

​ 有返回值类型 所有数据类型

​ 无返回值 void

​ 1.4方法名(驼峰):唯一的,不可重复的

​ 1.5(形式参数):任意数据类型,有返回参数必须return相同类型返回值

​ 1.6 {方法体}

2.调用方法:

​ 静态方法和c一样。

仅适用于数组:有参无返回值调用是直接调用地址,被调用变量的最终结果可能会被其他方法改变。(具体调用是否改变可见源码是否调用地址)

2.1 有多个参数(附卫语句例/登入):

public static void userLogin(String usename,String password){
    String name = "admin";
    String pass = "admin";
    
    //校验username与password
    if(username==null || password==null){
        System.out.println("参数不允许为null");
        return;
    }
    
    if(!neme.equals(username) || !pass.equals(password)){
        System.out.println("用户名或密码不对");
    }
    
    System.out.println("登陆成功");
    System.out.println("执行成功之后的代码。。。。。");
}

2.2可变参数:

test();

//可变参数->同一种数据类型 例:数据类型...变量名
//nums  是可变参数 >=0个实际参数 实际为一个数组
public static void test(int...nums){
    System.out.println(nums[0]);
    System.out.println(nums[1]);
    System.out.println("nums:"+Arrays.toString(nums));
}

2.3 方法重载 overload

在一个类(java文件),可以存在多个方法名一样的方法。

  1. 方法名相同
  2. 形参的顺序(形参列表不一致)
  3. 与返回值和修饰符无关
  4. 可维护性及可扩展性变差

(类与对象)

1.不同方法调用同一个成员变量?

创建成员属性

2.无参&有参构造方法

​ 快捷键:alt+shift+s+c(生产无参构造)

​ 例:调用带参/无参构造方法

public class Person {
    
    public int id;
    public String name;
    
    //默认的无参构造方法
    //构造方法与类名一致
    //[访问权限修饰符] 类名() {}
    
    //默认存在
    //1.帮助创建对象
    //2.初始化成员变量
    public Person() {
        // id = 1001;
        // name = "jim";
    }
    //在创建对象的时候就对成员变量赋值  有参构造
    //方法重载
    //方法名相同 形参列表不同
    //与返回值 修饰符无关
    //构造方法重载
    public Persion(int pid,String pname){
        id = pid;
        name = pname;
    }
    }
}
public class PersonTest {
    public static void main(String[] args){
        Person person = new Person();
        System.out.println("姓名"+person.name);
        //使用有参构造进行创建对象
        Person person2 = new Person(1002,"tom");
        System.out.println("person2姓名:"+person2.name);
    }
}

成员变量 = 全局变量

(封装)

1.默认修饰符只能在本包内访问

​ 修饰符为public时可以访问别的包的成员变量

2.private:只能在本类中被调用

​ setter&getter中不允许添加其他业务逻辑,会增加维护难度

​ 快捷键生成:alt+shift+s+r

3.lombok

3.1.在使用@Setter能否再另外添加别的set方法?

​ a:可以,手动写的可以覆盖掉注释里的Setter/Getter

3.2.lombok.jar类库文件

​ 通过注解构建一个全功能的文件:@名称

​ 例:@Sette @Getter(生成set/get方法)

​ @NoArgsConstructor(生成无参构造)

​ @AllArgsConstructor(生成有参构造)

​ @ToString

​ @cleanup(程序运行之后关闭input,不建议)

​ @Override(重写父类)

​ @EqualsAndHashCode(生成hashcode)

​ @Data(里面包含@Sette 、@Getter、@ToString、@EqualsAndHashCode)

4.static(作用:修饰成员变量和成员方法)

4.1.静态方法只能访问静态成员

​ 静态成员变量数据是共享的(且只能在本类中调用,使用其他类调用时需要先调用静态成员变量所在方法)

4.2.*静态代码块:static{代码} //有且只走一次

4.3.非静态代码块:{代码}

4.3.优先度

​ 静态代码块 > 非静态代码块 > 构造方法

4.4.static修饰成员变量

​ static修饰成员变量时,该变量数据可以让所有类的对象共用。

5.单例模式/饿汉模式/懒汉模式

​ userinfo&~test

5.1.单例模式

​ 1.将构造方法改为prevate修饰

​ 2.提供一个静态的成员变量,并初始化成员变量

​ 3.提供静态方法,获得此类的对像

5.2.饿汉模式(开发不推荐)

private static ShoopingCar shoppingCar = new ShoppingCar;
public static ShoppingCar gatShoppingCar(){
	return shoppingCar;
}

​ 1.不管是否使用变量,都会执行初始化操作,没有体现出懒加载的思想

​ 2.没有线程安全问题

5.3懒汉模式

private static ShoppingCar shoppingCar;
public static ShoppingCar getShoppincar(){
    if(shoppingCar==null){
        shoppingCar = new ShoppingCar;
    }
    return shoppingCar;
}

​ 1.什么时候使用对象 什么时候执行初始化操作

​ 2.有线程安全问题

5.4枚举(多例/开发常用)

6.this

6.1.在本类里访问无参/有参构造:

​ 使用:this(); //必须在代码第一行

6.2.this可以充当实际参数

​ (了解)因为他是个对象。

(继承:extends)

1.子类实例化

优先执行父类无参构造

2.super(只能在构造方法的第一行)

在子类构造里 默认使用super关键字调用了父类的无参构造 super()

如果父类没有提供无参构造 子类一般也不会提供无参构造

没有无参构造的解决办法:super(变量,变量,变量)

3.protected

本类及其子类可以访问(父子友好),同一个包中的其他类也可以访问(包内友好)

4.重写(override)

5.时间转String

String dateStr = dstr.format(d1);//

Date date = new Date(); //时间转String
		String dateStr = sd.format(date);//

(多态)

1.编译时数据类型与运行时数据类型不一致的时候

​ = 左边的数据类型 编译时诗句类型(jvm加载的 = 左边的数据类型的class文件)

​ = 右边的数据类型 运行时的数据类型(真正运行的逻辑要看右边的类型)

public static void main(String[] args){
    PM pm = new PM(1,"jim",'m',6000,6,2000);
    SE se = new SE(2,"jim2",'m',5000,1);
    Sale salae = new Sale(3,"jim3",'m',3000);
    
    Pay.pay(pm);
    Pay.pay(se);
    Pay.pay(sale);
}
public class Pay{
    public static void pay(Employee employee)//Employee是父类
    double salary = employee.getSalary();
    System.out.println(""+employee.getName....);
}

2.父类对象的向下转型成一个子类对象

PM pm =  (PM)employee;
double bonus = pm.getBonus();
salary+=bonus

3.instanceof

​ instanceof 判断对象是否是一个类的实例(对象) 判断employee是否是PM的实例

if(employee instanceof PM){
    PM pm = (PM)employee;
    double bonus = pm.getBonus();
	salary+=bonus
}

4.对象数组

​ 数组元素是一个个的对象

Employee[] emplpyee = {empoyee1,empoyee2,empoyee3};

4.1.遍历数组对象

​ 用到jdk1.7新特性

//objects
Objects.requireNonNull(数组对象);
for(Employee emp:employees){
    pay(emp);//某个show方法
}

5.抽象类(abstract)

​ 子类必须使用@Override重写

​ 一个类里有抽象方法的话,这个类一定是抽象类

​ 抽象类必须要有构造方法(服务于子类对象的创建、多态)

public abstract calss AbstractClass {
	//抽象类就比普通类多了一个抽象方法
    public AbstractClass(){}
    //不能创建对象 不能实例化
    //体现多态
    public void a(){
        system.out.println("aaaa");
    }
    public abstract void b();
}

6.接口(interface)

6.1使用:[访问权限修饰符] interface 接口名称 {}

​ 接口没有构造方法,所以不能实例化、创建对象

6.2接口修饰的类里只能有常量和功能方法,且常量前会自动加上"public static final"修饰

6.3常量名必须大写

6.4 jdk1.8之前(接口里只能有抽象方法)

例
void b(int num); //实际就是public abstract void b(int num);
//一般开发中不写public abstract

6.5(1.8之后)

​ 接口里既有抽象方法,也可以有普通的功能方法,但是要用default/static修饰

6.6.使用接口

​ 单继承:extends

6.6.1.类实现多接口,一个类课实现多个接口 (implements)

​ 接口的实现类

public class 类名 implements 接口{}

6.6.2.调用父接口里的常量或方法

​ 父接口.super.常量/方法

6.6.3.接口实现多个接口

​ 接口可以继承多个接口

public interface MyInterface extends A,B{}

6.6.4.接口命名

​ 对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别

(异常)

1.final

​ 修饰成员变量 public static final 常量 (名称全部大写 必须赋值)

2.异常

2.1所有错误和异常的父类(Throwable)

​ 错误:Error(无法解决,一般与jvm有关系)

​ 异常:Exception(可以处理)

​ java的健壮性:GC(垃圾回收)

​ 异常的处理(异常的初夏,不影响其他程序正常执行)

​ jdk的api文档:application program interface 应用程序接口(类、接口、枚举类)

String getMessage()   //获得异常的字符串内容 使用e.getMessage
void printStackTrace() //将异常详细信息输出到控制台 使用e.printStackTrace();(最常使用)
Throwable getCause() //如果原因不存在或未知,则返回throwable的原因或null

2.2异常分类

​ 1.可检测到的异常 checked Exception

​ 2.运行时异常 Runtime Exception (除了这个其他都是编译时的异常/可检测的异常)

2.3一些常见的异常

ArryIndexoutOfBuondsException(数组指针越位异常)

ArithmeticException(算数异常)

3.处理异常

3.1.捕获

try{
    //嫌疑代码
}catch{
    //处理方式(只走一个)
}finally{
	//其他注释(必走)
}

3.2抛出(消极处理异常)

把异常抛给调用者,抛出给上级,并不会允许错误后的代码继续允许

public void a() thows NullpointerException,PaeseException,其他异常 {
    
}

4.自定义异常

jdk提供的异常类并不满足开发需求的时候,可以选择自定义异常;
public class DatePatternException extends RuntimeException{

	public DatePatternException() {
		super();
	}

	public DatePatternException(String message, Throwable cause) {
		super(message, cause);
	}

	public DatePatternException(String message) {
		super(message);
	}

	public DatePatternException(Throwable cause) {
		super(cause);
	}
}

(lang包)

1.java.lang*包

1.1.Integer

1.2Character类

1.3.object

object是类object结构的根

构造:object()
功能方法:
protected object clone() 克隆对象(创建对象) 克隆对象的副本 默认是浅克隆
boolean equals(object obj) 判断对象的数据是否一致
Class<?> getClass()  获得正在运行的类或者接口的class文件

nt hashCode()   获得对象的哈希码值

String toString()  将对象转换成字符串进行操作    

protected void finalize()   回收对象
void notify() /void notifyAll()   在另外一个线程里面唤醒已经等待的线程 
void wait() / void wait(long timeout)   / void wait(long timeout, int nanos) 
    让当前线程处于等待

2.克隆(面试)

克隆时必须实现Cloneable接口

2.1浅克隆

慎用克隆操作

如何调用克隆后类的方法?(体现浅克隆)

a:重写父类的clone方法

弊端:基本数据类型/包装类/String 复制的是数据,但是其他类型是复制地址值(如数组、类对象),导致数据共用。

@Override
public User clone(){
    User user = null;
    try{
        user = (user) super.clone();
    }catch(CloneNotSupportedException){
        e.printStakTrace;
    }
    return user;
}
java.lang.CloneNotSupportedException: com.javasm.lang.User
	at java.lang.Object.clone(Native Method)
	at com.javasm.lang.User.main(User.java:26)
	
	需要让类 实现  Clonable的接口
	
	public class User implements Cloneable{
        private Integer uid;
        private String uname;
        
    //重写父类的clone方法
    //权限修饰符改为public  方便其它类使用 
    //返回值改成User   本身就是在User类里面进行克隆 肯定是User类的对象 
	@Override
	public User clone()  {
		User user = null;
		try {
			user =(User) super.clone();//浅克隆
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return user;
	}
	}

2.2深克隆(遍历式copy)

解决浅克隆数据共用问题:

在克隆方法里为引用数开辟新的内存

student克隆student2
@Override
public Student clone(){
    Student student = null;
    try{
        student = (Student) super.clone();
        //修改hobby数据
        String[] newHobby = Arrys.copyOf(this.copy,hobby.length);
        student.setHobby(newHobby);
    }catch(CloneNotSupportedExcaption e){
        e.printStackTrace();
    }
}

3.获得Class对象3种方式(重点)

1. Class<?> getClass()  获得正在运行的类或者接口的class文件
    User user = new User();
    Class clazz = user.getClass(); //clazz获得了user的所有内容(属性和方法和构造方法)
2. 每个类/接口/枚举类  都有一个成员变量  static class属性       类名.class;
	Class clazz2 = User.class;
3. 利用Class类的静态方法  Class.forName("类/接口的路径");
	Class clazz3 = Class.forName("com.javasm.lang.User");//要使用try cath操作

3.1class

  • Class类的类表示正在运行的Java应用程序中的类和接口
  • 反射的根本 反射的实现是通过Class来实现的
  • 反射: 通过Class的对象 操作类或者接口的属性 方法 构造
  • 直白来说:在反射里面,将类的属性封装成Field进行操作
  • 将类的方法封装成Method进行操作
  • 将类的构造封装成Constructor进行操作

4.反射(为所欲为)reflect

需要调包:import java.lang.reflect.InvocationTargetException;

Class将其他类中的属性封装成Field、方法Method、构造Constructor来进行操作

@Setter@Getter
public class Teacher {

	public Integer num;

	private String str = "hello";
	protected double dou;

	private String tname;
	private Integer tid;
    private void b(int num) {
		System.out.println("b.........." + num);
	}
}
//访问/修改成员属性

Field[] field1 = clazz.getFields();  //只能获得public所修饰的成员变量
Field[] field2 = clazz.getDeclaredFidlds();//类所有的成员变量
//---------------------修改
try{
    for(Field field:field2){
        if(field.getName().eauals("tname")){
            field.setACCESSIBLE(true); //开启访问权限(如private、protected所修饰的)
            field.det(teacher."jim") //赋值  teacher是对象引用
        }else if(其他要修改的field.get***().eauals("***")){
            field.setACCESSIBLE(true);
            field.det(teacher."***")
    }
}
//------------------访问
try{
    Field field1 = clazz.getDeclaredField("str");//teach类里有String str = "hello"
    field1.setAccessible(true);
    //获取get
    String obj = (String)field.get(teach);
    Systen.out.println("obj:"+obj);
    Systen.out.println(field1.getType());
}catch{
    //有较多可捕获异常,因为安全级别较高
}catch{
    
}

//访问/修改成员方法--------------------------------------------------------
public static void main(String[] args) {
		Teacher teacher = new Teacher();
		// 1.获得Teacher类的class内容
		Class clazz = Teacher.class;
		// 2.操作Teacher类的成员方法   Method
		//操作方法对属性赋值
		//对tname进行赋值  setTname
		
		Method[] methods1 = clazz.getMethods();//public (子类+父类)(没有私有的)
		Method[] methods2 = clazz.getDeclaredMethods();//(子类里面所有的方法,包括私有的)

		//后期学习的内容
		//Mybatis 动态代理(JDK代理)
		//Spring AOP 
		
		try {
			//可变参数: >=0实参
			Method method = clazz.getMethod("setTname", String.class);
			//3.执行(唤醒)方法  method  体现到对象里面
			Object obj = method.invoke(teacher, "张三");//与调用方法返回值类型有关
			System.out.println("obj:"+obj);
            
			//访问修改私有方法
			method = clazz.getDeclaredMethod("b", int.class);
			method.setAccessible(true);
			method.invoke(teacher, 100);

		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			e.printStackTrace();
		} 
		System.out.println(Arrays.toString(methods1));
		System.out.println(Arrays.toString(methods2));

		System.out.println(teacher);
	}
//基于Class创建对象--------------------------------------------------------
try {
			Object obj = clazz.newInstance();//基于类的无参构造创建的对象
			System.out.println("obj:"+obj);
			System.out.println(clazz.getName());
			System.out.println(clazz.getSimpleName());//类名  接口名
		} catch (InstantiationException | IllegalAccessException e1) {
			e1.printStackTrace();
		}
//		
		// 2.操作Teacher类的构造方法   
		//创建对象
		Constructor[] constructors1 = clazz.getConstructors();//public
		System.out.println(Arrays.toString(constructors1));
		Constructor[] constructors2 = clazz.getDeclaredConstructors();//所有
		System.out.println(Arrays.toString(constructors2));
		
		try {
			Constructor constructor = clazz.getDeclaredConstructor(Integer.class,String.class);
			constructor.setAccessible(true);
			Teacher teacher2 = (Teacher)constructor.newInstance(100,"jim");
			System.out.println("teacher2:"+teacher2);
			
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

(String/正则)

1.Sring

特征:
字符串不变; 它们的值在创建后不能被更改
线程安全。

只要是线程安全的类,都可以作为成员变量来使用。

1.1 常用构造方法

String(byte[] bytes) 
String(byte[] bytes, Charset charset) == String(byte[] bytes, String charsetName) 
//一般对于字符串乱码,会使用带参构造进行解码处理。使用指定编码格式对字节数组进行解码 转换成String
//Charset(抽象类,在java.nio包,可以用来输出当前编码格式)
String(char[] value) 
String(String original) 
String(StringBuffer buffer) 
String(StringBuilder builder) 

1.2常用功能方法

与数组转换:

byte[] getBytes() //使用系统默认编码格式对字符串进行编码操作 将结果存储到新的字节数组中
byte[] getBytes(Charset charset)  ==  byte[] getBytes(String charsetName)  
//使用指定编码格式对字符串进行编码操作 将结果存储到新的字节数组中

char[] toCharArray()  //将字符串转换成字符数组
    
String[] split(String regex)  //使用指定正则将字符串分割成字符串数组
String[]  split(String regex, int limit) 
limit: 字符串数组的元素个数(特殊:当limit为0时,会自动把最后一个空串过滤掉)
判断:例:数组.isEmpty();
    boolean isEmpty()  判断是否是空串
    boolean endsWith(String suffix)  判断字符串是否以指定内容结尾   文件名是否以某个后缀结尾
    boolean startsWith(String prefix)  判断字符串是否以指定内容开头
    boolean equals(Object anObject)  
    boolean equalsIgnoreCase(String anotherString )不区分大小写比较  验证码的比较
    int compareTo(String anotherString)  
    转成字符数组比较  返回内容是2个字符相减的结果   =0证明相等
    int compareToIgnoreCase(String str)
    boolean contains(CharSequence s)  判断原字符串是否包含指定字符串 (不推荐使用,效率偏低,因为需要在底层遍历两次)
    (重点)boolean matches(String regex)    指定字符是否匹配正则表达式要求 
获得部分字符串内容:
char charAt(int index)  获得指定索引的字符内容  注意索引范围
String substring(int beginIndex)  从beginIndex开始到最后进行截取 
String substring(int beginIndex, int endIndex)  
    从beginIndex开始到endIndex进行截取   包头不包尾
    
替换:
String replace(char oldChar, char newChar)  使用newChar替换原字符串里面所有的oldChar
String replace(CharSequence target, CharSequence replacement)   
    使用replacement替换原字符串所有的target
String replaceAll(String regex, String replacement)  
String replaceFirst(String regex, String replacement)   
int length()  
int indexOf(String str)   从第一个元素开始 查找指定字符串第一次出现的索引位置     找不到 返回-1
int indexOf(String str, int fromIndex)  从指定的索引开始查找指定字符串第一次出现的索引位置
int lastIndexOf(String str)  
    从第一个元素开始 查找指定字符串最后一次出现的索引位置     找不到 返回-1
int lastIndexOf(String str, int fromIndex)
    
String intern()  获得当前对象的副本   获得的就是常量池的地址
当intern方法被调用时,先去查看常量池是否有当前字符串内容,如果有的话,直接返回常量池地址。‘
没有的话,先将当前内容存储至常量池,再将常量池地址返回。

String trim()  去除字符串首尾空格

intern(面试)

在调用”ab”.intern()方法的时候会返回”ab”,但是这个方法会首先检查字符串池中是否有”ab”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。

2.正则表达式

意义:普通字符串内容

作用:用来校验/替换与正则模式相符的文本内容

在替换时不允许加"^&"开头结尾

如:replaceAll("\s"{1,},"");

2.1语法:

替换
String str = "aaabbbccc";
regex = "(.)\\1+";
String end = str.replaceAll(regex,"$1")
结果:abc
//先用Pattern检验正则表达式语法是否正确
Pattern pattern = Pattern.compile(正则表达式);
//再用Matcher创建比较器对象
Matcher matcher = pattern.matcher(需要校验的表达式)
//校验 匹配
Systm.out.println(matcher.matcher());
//或者
需要校验的表达式.matches(正则表达式) //返回boolean类型
"^([]{})([]{})([]{})$"
    [^]: 以...开头 
    [$]: 以 ...结尾
    [()]: 域段
    []: 文本内容
    {}:限定[]数据出现的次数
    $1:获得第一个域段里面的数据
场景:一般都是校验用户录入的数据是否符合正则要求。
* + ?   == [a]{1,}
hdsgeaaaabaaaaa
String类里面支持正则表达式的方法:
String[] split(String regex)
String replaceFirst(String regex, String replacement) 
    
    
String replaceAll(String regex, String replacement)  
boolean matches(String regex)    指定字符是否匹配正则表达式要求 

3.StringBuffer/Builder

String的弊端:在拼接字符串的时候会浪费内存 产生很多无用的字符对象。

StringBuffer

​ 特征:

​ 1.线程安全。所有的方法都加锁了(有且只有一个线程可以访问)

​ 2.可变的字符对象

​ 3.效率最低

StringBuilder(不涉及到线程安全时优先使用):

​ 特征:

​ 1.可变的字符对象

​ 2.线程不安全

​ 3.效率最高

1. 线程是否安全
    String,StringBuffer 安全
    StringBuilder 不安全
2. 数据是否可变
    String 不可变
    StringBuffer/StringBuilder 可变  只有一个对象
3. 效率高低
    StringBuilder->String->StringBuffer
4. 占用内存大小
    拼接:
       String 占用内存最大的
       StringBuilder/StringBuffer 较小

(日期)

*LocalDate/LocalTime/LocalDateTime

LocalDate 获得当前系统的时间 2007-12-03

LocalTime 获得当前系统的时间 10:15:30

LocalDateTime 获得当前系统的时间 2007-12-03T10:15:30 。

此类也是不可变且线程安全。jdk1.8

1.java.util.Date

特定的时间。

常用构造:
Date() 获得当前系统的时间
Date(long date)  结合毫秒数转换成Date类型的数据
常用方法:
long getTime()  获得时间的毫秒数
Instant toInstant()  
static Date from(Instant instant)  Date与Instant相互转换

2.java.time.LocalDate

LocalDate 是一个不可变的日期时间对象,表示日期,通常被视为年月日。 也可以访问其他日期字段,例如日期,星期几和星期。

方 法 原 型 说 明
LocalDate now() 返回当前系统时间
LocalDate now(Clock clock) 从指定的时钟中获得当前时间
LocalDate of(int year, int month, int dayOfMonth) 给定年月日,返回一个LocalDate实例
boolean isAfter/Before(ChronoLocalDate other in) 判断两个时间先后
boolean isLeapYear() 判断是否是闰年
LocalDate minus(TemporalAmount ta) 返回减去具体时间的LocalDate实例
LocalDate minusDays(long daysToSubtract) 返回减去具体天数的LocalDate实例
long until(Temporal endExclusive, TemporalUnit unit) 计算两个时间的间隔转换成具体的时间单位
Instant with(TemporalField field, long newValue) 将指定的字段设置为新值返回Instant实例
常用方法:
    static LocalDate now() //获取现在时间
    static LocalDate of(int year, int month, int dayOfMonth)  //自定义时间
    static LocalDate of(int year, Month month, int dayOfMonth)  
    LocalDate plus(long amountToAdd, TemporalUnit unit)  //TemporalUnit unit时间单位(增加时间)
    LocalDate plusDays(long daysToAdd)  
    LocalDate plusMonths(long monthsToAdd)  
    int getDayOfMonth()  //获取一个月的第几天
    DayOfWeek getDayOfWeek()  //获取一周的第几天
    Month getMonth()  	
    int getYear()  
    int getMonthValue()  //此方法将一个月作为int从1到12返回
    static LocalDate parse(CharSequence text, DateTimeFormatter formatter)  //使用特定格式化LocalDate从文本字符串获取LocalDate的实例。使用格式化程序解析文本,返回日期。

    public Period until(ChronoLocalDate endDateExclusive)//这将计算两个日期之间的年份
       ChronoLocalDate:

3.DateFormat(重点)

抽象类。格式化Date类型的数据。

SimpleDateFormat : 格式化和解析日期   线程不安全(不能作为成员变量使用)
String 类型时间   与   Date 类型的时间相互转换
pattern: yyyy-MM-dd  HH:mm:ss E a
public class DateUtil {

	private DateUtil() {
	}
    private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
    /**E
	 * @param date 日期对象
	 * @return 格式化之后的字符串日期
	 */
	public static String dateConvertToStr(Date date) {
		Objects.requireNonNull(date);
		DateFormat dateFormat = new SimpleDateFormat(PATTERN);
		return dateFormat.format(date);
	}
    /**
	 * @param dateStr 字符串日期
	 * @return 解析成功之后的Date对象
	 */
	public static Date strConvertToDate(String dateStr) {
		Objects.requireNonNull(dateStr);
		try {
			DateFormat dateFormat = new SimpleDateFormat(PATTERN);
			return dateFormat.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}
测试:	
public static void main(String[] args) {
//		System.out.println(DateUtil.dateConvertToStr(new Date()));
//		System.out.println(DateUtil.strConvertToDate("2019-10-12 12:00:00"));
}
由于SimpleDateFormat线程不安全,不能作为成员变量使用,因为在多线程的环境下,会产生数据不安全的问题。
当然,也有解决的方式:
      保证每个线程都有各自的一个 SimpleDateFormat 对象就可以了。
public class DateUtil {

	private DateUtil() {
	}
	private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
	// FORMAT_LOCAL 管理SimpleDateFormat的实例
	// 保证每个线程都有一个SimpleDateFormat的一个对象
	private static final ThreadLocal<SimpleDateFormat> FORMAT_LOCAL = new                   ThreadLocal<SimpleDateFormat>() {
		@Override
		protected SimpleDateFormat initialValue() {
			return new SimpleDateFormat(PATTERN);
		}
	};
	
	public static String dateConvertToStr(Date date) {
		Objects.requireNonNull(date);
		return FORMAT_LOCAL.get().format(date);
	}
	
	public static Date strConvertToDate(String dateStr) {
		Objects.requireNonNull(dateStr);
		try {
			return FORMAT_LOCAL.get().parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}
在多线程的环境下进行测试:
public static void main(String[] args) {
   String[] dateStrs = { "2019-10-12 12:00:00", "2019-10-12 12:00:00", "2019-10-12 12:00:00",
				"2019-10-12 12:00:00", "2019-10-12 12:00:00", "2019-10-12 12:00:00", "2019-10-12 12:00:00",
				"2019-10-12 12:00:00", "2019-10-12 12:00:00", "2019-10-12 12:00:00", "2019-10-12 12:00:00",
				"2019-10-12 12:00:00" };

        // 多线程
		new Thread(() -> {
			for (String str : dateStrs) {
				System.out.println(DateUtil.strConvertToDate(str));
			}
		}).start();// 1个线程

		new Thread(() -> {
			for (String str : dateStrs) {
				System.out.println(DateUtil.strConvertToDate(str));
			}
		}).start();// 1个线程
}

4.DateTimeFormatter(重点)

  • 格式化器用于格式化日期时间对象(Instant LocalDate/Time/DateTime)
  • 类不可变且线程安全
static DateTimeFormatter ofPattern(String pattern)  创建DateTimeFormatter对象
TemporalAccessor parse(CharSequence text) 
String format(TemporalAccessor temporal)  

5.NumberFormat

格式化数字。最后的结果数据: String

DecimalFormat 子类 对十进制的数据进行格式化操作。

DecimalFormat(String pattern)   #/0/.
//#.##  保留两位小数
//#.##%  自动百分比计数并保留两位小数
//,### 每三位以逗号分隔

    

6.Calendar

  1. lCalendar类也是用来操作日期和时间的类,但它可以以整数形式检索类似于年、月、日之类的信息;
  2. lCalendar类是抽象类,无法实例化,要得到该类对象只能通过调用getInstance方法来获得;
  3. lCalendar对象提供为特定语言或日历样式实现日期格式化所需的所有时间字段。
实例:
public class CalendarDemo
{
  public static void main(String[] args)
  {
    //创建包含有当前系统时间的Calendar对象
    Calendar cal = Calendar.getInstance();
    
    //打印Calendar对象的各个组成部分的值
    System.out.print("当前系统时间:");
    System.out.print(cal.get(Calendar.YEAR) + "年");
    System.out.print((cal.get(Calendar.MONTH) + 1) + "月");
    System.out.print(cal.get(Calendar.DATE) + "日 ");
    System.out.print(cal.get(Calendar.HOUR) + ":");
    System.out.print(cal.get(Calendar.MINUTE) + ":");
    System.out.println(cal.get(Calendar.SECOND));
    
    //将当前时间添加30分钟,然后显示日期和时间
    cal.add(Calendar.MINUTE, 30);
    Date date = cal.getTime();
    System.out.println("将当前时间添加30分钟后的时间:" + date);
  }
}

7.Insatnt(jdk1.8)

JDK8的应用,推荐使用 Instant 代替 Date。

LocalDateTime 代替 Calendar,

Instant 时间线上的瞬间点,采用UTC的时间刻度。

此类是不可变的且是线程安全的。

在java.time.*包下 1.8 版本才有。

实例:
Insatnt now()  获得的是格林尼治的时间  与北京时间差8个小时 加上8个小时

Instant instant = Instant.now();// 2019-03-14T06:33:13.034Z
instant = instant.plus(Duration.ofHours(8));//与北京相差8个时区  Duration是一个实现类
instant = instant.plus(8, ChronoUnit.HOURS);
 instant = instant.plusSeconds(TimeUnit.HOURS.toSeconds(8));
  instant = Instant.now(Clock.offset(Clock.systemDefaultZone(), Duration.ofHours(8)));
  System.out.println(instant);

   int second = instant.get(ChronoField.NANO_OF_SECOND);
   Instant instant1 = instant.minus(4, ChronoUnit.DAYS);

   long duration = instant.until(instant1, ChronoUnit.HOURS);//计算2个时间间隔  转换成相对应的时间单位

        //将Instant  转换成string  DateTimeFormatter
   DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
   String str = formatter.format(instant1);

   //String 转换成 Instant
   Instant parse = Instant.parse("2019-03-14T06:33:13.034Z");//默认是这种格式的时间  否则无法转换

(集合)

1.针对Collection集合我们到底使用谁呢?

唯一吗?

​ 是:Set

​ 排序吗?

​ 是:TreeSet或LinkedHashSet
​ 否:HashSet
如果你知道是Set,但是不知道是哪个Set,就用HashSet。

否:List

​ 要安全吗?

​ 是:Vector
​ 否:ArrayList或者LinkedList

​ 查询多:ArrayList
​ 增删多:LinkedList
如果你知道是List,但是不知道是哪个List,就用ArrayList。

如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
如果你知道用集合,就用ArrayList。

2.Collection

  • 集合层次结构中的根接口 。
public interface Collection<E>  extends Iterable<E>
某些类  某些接口 <> 泛型的标志
集合: 使用泛型  限定集合元素的数据类型。(代表集合里面只能存储相同类型的数据)
泛型修饰类 接口 方法
泛型类  泛型接口  泛型方法

常用功能方法:
    boolean add(E e)  新增元素到集合
    void clear()  清空集合所有元素
    boolean contains(Object o)  判断集合是否包含一个元素
    boolean isEmpty() 判断集合是不是空
    Iterator<E> iterator() 获得集合的迭代器对象  从Iterable继承的
      Iterator:
        boolean hasNext()   判断光标后面是否有更多元素需要遍历
        E next()   获得光标之后的元素内容
        default void remove()  删除光标之后的数据
    
    boolean remove(Object o)  删除集合元素
    int size()  获得集合元素个数==>length
    Object[] toArray()  将集合转换成数组
    <T> T[] toArray(T[] a)  
    
    //jdk1.8 
    default void forEach(Consumer<? super T> action) 遍历集合所有元素
    default boolean removeIf(Predicate<? super E> filter)  删除多个集合元素
    default Stream<E> stream()  获得集合对象的Stream  并行化操作
lamda表达式:
语法:
(数据类型 形参1,数据类型 形参2)->{
    //功能
}

使用场景:
   1. 运用在集合遍历  forEach()
   2. 取代匿名内部接口对象
   
   Collection<Integer> collection = new ArrayList<Integer>();
		collection.add(1);
		collection.add(10);
		collection.add(11);
		collection.add(11);
遍历Collection集合元素:
default void forEach(Consumer<? super T> action) 遍历集合所有元素
Consumer是一个接口,接口本身不能new ,但是new的话也可以,我们一般称为接口的匿名内部类对象。

collection.forEach(new Consumer<Integer>() {//new了一个接口对象  重写接口里面的抽象方法
			@Override
			public void accept(Integer num) {
				System.out.println(num);
			}
	});
这种写法比较麻烦,可以使用lamda替换。
collection.forEach((Integer num)->{
			System.out.println(num);	
   });
等同于:把重写的accept方法简化了,accept只有一个形参,所有lamda的话也必须只有一个形参。
在接口抽象方法只有一个形参的时候,() 以及数据类型都可以选择不写:
collection.forEach(num->{
			System.out.println(num);
 });

不能在遍历的时候调用集合里面的remove/add等操作,否则会出现并发修改的异常。

因为集合本身不支持并行化的操作,jdk1.8之后才支持。

并行:一个程序在同一个时间做多件事情。是计算机系统中能同时执行两个或多个处理的一种计算方法

3.List & Set

共同点: 存储单值元素。

元素是否有序 元素是否可重复
List 有索引位置 可以重复
Set 无序 元素是唯一的 不可重复

3.1.List接口

  • 有序集合(也称为序列
  • 通过整数索引(列表中的位置)访问元素,并搜索列表中的元素

常用实现类

底层数据结构 新增,查询,删除效率 线程安全
ArrayList(重点) 动态数组 查询最快 不安全
LinkedList 双向链表 新增和删除效率最快 不安全
Vector 动态数组 都慢 安全

常用方法

void add(int index, E element)  //在设定序列增加数据

E get(int index)  //读取某序列的数值

E remove(int index)  //删除某序列的数值

E set(int index, E element)  //修改某序列的数值 
List<E> subList(int fromIndex, int toIndex)  //返回某段数据
ArrayList() //初始化容量为十的空列表   动态数组的空间大小   1.5倍
ArrayList(Collection<? extends E> c) 
ArrayList(int initialCapacity) 
//规则  扩容   initialCapacity = (需要存储的元素个数 / 负载因子) + 1。   

3.2.Set 接口

  • 不包含重复元素的集合 且元素无序

  • 规则: 如果存储自定义对象 一定要重写equals和HashCode

    常用实现类

底层数据结构 元素是否可以为null 线程安全
HashSet(重点) 哈希表 可以
LinkedHashSet 链表+哈希表 可以
TreeSet 红黑树(二叉树) 不能存储null

HashSet/LinkedHashSet方法与父接口Collection一模一样

HashSet: 元素无序(顺序)

LinkedHashSet:

元素有序。(元素插入顺序与遍历顺序是一致的)

TreeSet

元素有序. (会按照自然顺序进行排列) 从小到大 String hashcode

规定元素数据类型必须实现Comparable接口

3.3.Map<k.v>

  • 将键K映射到值V的对象。 K不能重复; 通过key可以获取v的数据。 v值可以重复
  • <> 参数化数据类型 : 类类型
  • T E K V
  • Key value 键值对

常用实现类:

数据结构 k 与 v 是否可以null 线程安全
HashMap<k,v>(重点) 哈希表 都可以为null
LinkedHashMap<K,v> 哈希表+链表 都可以null
TreeMap<K,V> 红黑树 key 不能为null value可以
HashTable<k,v> 哈希表(sy) k与v都不能为null 安全
ConcurrentHashMap<k,v>(重点) 1.7 锁分段技术 1.8CAS(乐观锁) k与v都不能为null 安全
Properties(重点) 体现可以读取配置文件的内容(IO)
Set<Map.Entry<K,V>> entrySet()  获得map集合里面所有的元素存储到set集合  优先使用
    将map集合的k和v封装成一个个的键值对entry 再将entry存储到set集合
    Entry:
        K getKey()  
        V getValue()  
        V setValue(V value)  

Set<K> keySet()  获得map集合里面所有的key值
    
default void forEach(BiConsumer<? super K,? super V> action)  
V get(Object key)  
V put(K key, V value)  
V remove(Object key)  
default boolean remove(Object key, Object value)  
default V replace(K key, V value) 
default boolean replace(K key, V oldValue, V newValue) 
boolean containsKey(Object key)  
int size()
Collection<V> values()

HashMap的元素完全无序的

LinkedHashMap元素可以预测的 key 插入的顺序与遍历顺序一致

TreeMap key值有序 按照自然顺序对key进行排列

4.集合元素排序

4.1.List排序

4.1.1.Collections

static <T extends Comparable<? super T>>  void sort(List<T> list) 
static <T> void sort(List<T> list, Comparator<? super T> c)  
static void shuffle(List<?> list)   
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 
static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp)  

4.1.2.Comparable

内部比较器:
  private static void testSortList2() {
		List<User> userList = new ArrayList<>();

		userList.add(new User(0, "tom"));
		userList.add(new User(10, "jim1"));
		userList.add(new User(10, "jam"));
		userList.add(new User(21, "abc"));
		userList.forEach(user -> {
			System.out.println(user);
		});
		System.out.println("---------------------------");
		// 元素必须实现Comparable接口
		Collections.sort(userList);
		userList.forEach(user -> {
			System.out.println(user);
		});
	}
调用sort()方法的时候会报错,要求集合的元素类型必须实现Comparable接口
public class User implements Comparable<User>{
    @Override
	public int compareTo(User user1) {
//		return this.name.compareTo(user1.getName());//升序
//		return user1.getId().compareTo(this.id);
		//先根据id比较  id值一样 再根据name比   自己制定排序规则
		int result = this.getId().compareTo(user1.getId());
		if(result==0) {
			result = this.name.compareTo(user1.getName());
		}
		return result;
	}
}

4.1.3.Comparator

外部比较器: java.util.*
private static void testSortList3() {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person(0, "tom"));
		personList.add(new Person(10, "jim1"));
		personList.add(new Person(10, "jam"));
		personList.add(new Person(21, "abc"));
		
		Collections.sort(personList, (Person person1, Person person2) -> {
			int result = person1.getId().compareTo(person2.getId());
			if (result == 0) {
				result = person1.getName().compareTo(person2.getName());
			}
			return result;
		});
}

4.2.Set排序

HashSet/LinkedHashSet本身就是无序 无法对元素顺序排列

只能针对TreeSet元素排列(要求Treeset集合元素类型都必须实现Comparable)

TreeSet(Comparator<? super E> comparator) 

4.3.Map排序

HashMap/LinkedHashMap的key本身就是无序 无法对元素顺序排列

只能针对TreeMap元素排列(要求map的key值类型都必须实现Comparable)

TreeMap(Comparator<? super K> comparator) 

5.Stream

Stream 和集合的另一个差异在于迭代。外部迭代需要亲自遍历数据,内部迭代只需要给出指令

流和迭代器类似,只能迭代一次。

List<String> newList = list.Stream().map(Person::getName).sorted().limit(10).collect(toList);
                (Stream将集合转换为流)  			(sorted执行排序)	      (coollect将流转换为集合)
								   						(limit保留前10个元素)
						(map将LIst<Person>转换为List<String>
						,其中String原素为Person的name成员变量)
filter() //保留 boolean 为 true 的元素  filter(person -> person.getAge() == 20)
distinct()  //去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的,需要先定义好 equals 方法
sorted() / sorted((T, T) -> int)  //需要实现Comparable or 手动实现 Comparator 下例16-23行
limit(long n)  //返回前 n 个元素
skip(long n) //去除前 n 个元素
//用在 limit(n) 前面时,先去除前 m 个元素再返回剩余元素的前 n 个元素
//limit(n) 用在 skip(m) 前面时,先返回前 n 个元素再在剩余的 n 个元素中去除 m 个元素
 map(T -> R)  //将流中的每一个元素 T 映射为 R(类似类型转换)
 flatMap(T -> Stream<R>) //将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T) //用于组合流中的元素,如求和,求积,求最大值等
List<Person> list = new ArrayList<>();
list.add(new Person("jack", 20));
list.add(new Person("mike", 25));
list.add(new Person("tom", 30));
//person里包含
private String name;
private int age;
//first,集合转换为流
List list = new ArrayList();
// return Stream<E>
list.stream();
//保留年龄为 20 的 person 元素   filter(T -> boolean)
list = list.stream()
            .filter(person -> person.getAge() == 20)
            .collect(toList());
//根据年龄大小来比较: (已实现Comparable接口)
list = list.stream()
           .sorted((p1, p2) -> p1.getAge() - p2.getAge())
           .collect(toList());
//未实现Comparrable接口 手动实现Comparator接口
list = list.stream()
           .sorted(Comparator.comparingInt(Person::getAge))
           .collect(toList());
//去除前n个元素
list = list.stream()
            .skip(2)
            .collect(toList());
//map(T->R)
List<String> newlist = list.stream().map(Person::getName).collect(toList());
//flatMap(T -> Stream<R>)
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(Collectors.toList());
//计算年龄总和:
int sum = list.stream().map(Person::getAge).reduce(0, (a, b) -> a + b);
//与之相同:
int sum = list.stream().map(Person::getAge).reduce(0, Integer::sum);
//lambda
Optional<Integer> sum = list.stream().map(Person::getAge).reduce(Integer::sum);

(IO流)

flush

将缓存区的内容刷出到文件里面

void flush()

1.File类

java.io.File

代表的是磁盘里面存在或者不存在的文件/目录(文件夹)。

1.1 常用构造:

File(File parent, String child)  parent: 目录  == File(String parent, String child) 
File(String pathname)    pathname:文件路径/文件夹路径(最常用)

1.2 常用方法:

代表文件时:
boolean exists()  判断文件是否存在
String getName()  获得文件名称
String getPath() 获得文件相对路径
long length()  获得文件字节大小
long lastModified()   获得文件上次修改时间的毫秒数
boolean createNewFile()   创建文件
boolean canRead()  文件是否可读
boolean canWrite()  文件是否可写
boolean setReadOnly()  标记由此抽象路径名命名的文件或目录,以便只允许读取操作。 
boolean setReadable(boolean readable) 如果true,设置访问权限允许读操作; 如果false不允许读操作 

boolean isFile()  
代表目录时:
boolean isDirectory()  
String[] list()  
String[] list(FilenameFilter filter)  
File[] listFiles()  
File[] listFiles(FilenameFilter filter)  
File[] listFiles(FileFilter filter)  
boolean mkdir()  创建1级目录
boolean mkdirs() 创建1级或者多级目录

1.3.递归

查看当前目录下所有的子目录和子文件:
testFile3(new File("src"),"|-");

private static void testFile3(File file,String pathStr) {
		File[] files = file.listFiles();//一级资源
		for(File fi:files) {
			if(fi.isDirectory()) {//判断是否是目录
				System.out.println(pathStr+fi.getName());
				testFile3(fi,"| "+pathStr);
			}else {
				//文件
				System.out.println(pathStr+fi.getName());
			}
		}
	}

1.4过滤

boolean endsWith(String suffix)   测试此字符串是否以指定的后缀结尾。 

2.字节流

------> try-with-resource语法

优雅的关闭流
public static void main(String[] args) {
    try (
    FileInputStream inputStream = new FileInputStream(new File("test"))
    ) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}
  1. 通过java程序将内容写入到磁盘里面。write --->输出流OutPut
  2. 通过java程序读取文件(txt)内容。 read--->输入流 Input

2.1 字节输入流-->InputStream

InputStream 抽象类 
    1. FileInputStream
    2. BufferedInputStream(高效流) 自带一个缓冲区 byte[]  8192
    3. ObjectInputStream
    4. DataInputStream
InputStream:
    int read()  从输入流中读取一个字节的数据
    int read(byte[] b)  从该输入流读取最多 b.length个字节的数据,存储到字节数组7
    int read(byte[] b, int off, int len)  从该输入流读取最多len字节的数据,存储到字节数组 
    void close()  关闭流
    int available()  获得输入流可读的字节个数
FileInputStream:
常用构造:
FileInputStream(File file) ;创建流对象  并与实际文件内容关联。
FileInputStream(String name);

2.2 字节输出流-->OutPutStream

OutPutStream 抽象类 
    1. FileOutPutStream
    2. BufferedOutPutStream(高效流)
    3. ObjectOutPutStream
    4. DataOutPutStream
OutPutStream:
void write(int b)  将指定的字节写入此输出流。
void write(byte[] b)  将 b.length字节从指定的字节数组写入此输出流。 
void write(byte[] b, int off, int len) 
    从指定的字节数组写入 len个字节,从偏移off开始输出到此输出流。 
void close()  
FileOutputStream:
FileOutputStream(File file) ;
FileOutputStream(File file, boolean append) ;
FileOutputStream(String name) ;
FileOutputStream(String name, boolean append) ;

3.字符流

3.1输入流

Reader:
1. FileReader
2. BufferedReader
3. InputStreamReader
int read()  一次读取一个字符   -1
int read(char[] cbuf)  
int read(char[] cbuf, int off, int len)  
void close() 
FileReader(File file) 
FileReader(String fileName) 

3.2输出流

Writer:
1.FileWriter
2.BufferedWriter
3.OutPutStreamWriter
Writer:
void write(int c)  
void write(char[] cbuf)  
void write(char[] cbuf, int off, int len)  
void write(String str)  最常用
void write(String str, int off, int len)  
Writer append(char c)  
void close()     
FileWriter:
FileWriter(File file) 
FileWriter(String fileName) 
FileWriter(File file, boolean append) 

4.其他流

4.1数据流 (了解)

​ 主要是对字面量数据类型的数据进行读写操作。

​ 必须先执行write

​ 使用unico编码,wread顺序必须要和write顺序一样(属实不行)

DataInput   DataInputStream 数据输入流

DataOutput  DataOutPutStream 数据输出流
DataOutputStream(OutputStream out) 
参照DataStreamDemo.java

4.2序列化流(重点)

数据会被覆盖

对象必须要实现Serializable接口才能序列化

主要是对对象进行读写操作。

必须先执行write

unico存储

ObjectInput ObjectInputStream  反序列化:读取文件里对象的数据
ObjectOut  ObjectOutputStream  序列化流:将对象写入文件  //读取时要强制转换成该对象
transient : 修饰属性  会导致属性不被序列化,即使我们给数据,反序列化的时候也看不到。
    一般会修饰安全型级别比较高的数据 比如:密码,余额等之类的。
 参照 ObjectStreamDemo.java

4.3转换流

是字符流与字节流转换的桥梁

字符流的子类:Reader
InputStreamReader   字节流转换成字符流的桥梁
InputStreamReader(InputStream in) 
InputStreamReader(InputStream in, String charsetName) 
    
OutPutStreamWriter  字符流转换成字节流的桥梁
OutputStreamWriter(OutputStream out) 
OutputStreamWriter(OutputStream out, String charsetName)    
如:用户在控制台录入的数据保存到文件里(System.in是字节流,将其转成字符流) (数据永久保存)
//字结转字符(字符转字节相反)
try(
    //Scanner input = new Scanner(System.in);
    BufferedReader b = new BufferedReader(nre InputStreamReader(System.in));
    BufferedWrider w = new BuffreadReader(new FileWride(new File("src/c.txt"))); 
){
    Systemout.ou.println("请循环录入");
    while(true){
        String str = b.readLine();
        if("end".equalsIgnoreCase(str)){ // 当录入为end时,跳出循环
            break;
        }
        w.wride(str);
        w.newLine;
    }
}

4.4Properties(重点)

核心功能: 主要是加载核心配置文件内容。读取配置文件的数据。属性名,属性值

配置文件: *.properties

数据的格式: 属性名 = 属性值 uname = jim

HashTable的子类。
Properties功能类似于map。存储一组元素,一般称为属性集。存储属性名和属性的值  key---value
void load(InputStream inStream)  
void load(Reader reader)      

(多线程)

1.术语解释

1.1 并行 vs 并发

共同点:在同一时间内都可以执行多任务。

并行(Parallelism):是计算机系统中能同时执行两个或多个处理的一种计算方法.并行处理的主要目的是节省大型和复杂问题的解决时间.
比如:你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行.大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。
比如: 你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。

1.2 同步 vs 异步

同步: 多个事物不能同时进行,必须一个一个的来,上一个事物结束后,下一个事物才开始.(在同一个时间内有且         只有一个程序在执行)
异步: 多个事物同时进行。不用等待其它程序是否有返回的结果。(在后面学习ajax的时候更能体现异步-->一般称为       异步请求)
注册: 用户名

1.3 阻塞和非阻塞

阻塞: 指的是阻碍堵塞。它的本意可以理解为由于遇到了障碍而造成的动弹不得。
非阻塞: 自然是和阻塞相对,可以理解为由于没有遇到障碍而继续畅通无阻。
对这两个词最好的诠释就是,当今中国一大交通难题,堵车:
汽车可以正常通行时,就是非阻塞。一旦堵上了,全部趴窝,一动不动,就是阻塞。
因此阻塞关注的是不能动,非阻塞关注的是可以动。回到程序里,阻塞同样意味着停下来等待 wait,非阻塞表明可以继续向下执行

1.4 组合使用

所谓同步/异步,关注的是能不能同时开工。
所谓阻塞/非阻塞,关注的是能不能动。

同步阻塞,不能同时开工,也不能动。只有一条小道,一次只能过一辆车,可悲的是还都堵上了。
同步非阻塞,不能同时开工,但可以动。只有一条小道,一次只能过一辆车,幸运的是可以正常通行。
异步阻塞,可以同时开工,但不可以动。有多条路,每条路都可以跑车,可气的是全都都的堵上了。
异步非阻塞,可以同时开工,也可以动。有多条路,每条路都可以跑车,很爽的是全都可以正常通行。

回到程序里,把它们和线程关联起来:
同步阻塞,相当于一个线程在等待。
同步非阻塞,相当于一个线程在正常运行。
异步阻塞,相当于多个线程都在等待。
异步非阻塞,相当于多个线程都在正常运行。	 

1.5 进程 vs 线程

进程:指系统中正在运行中的应用程序,它拥有自己独立的内存空间.
    简单来说: 一个运行的程序就是一个进程。
线程:指进程中一个执行流程,一个进程中允许同时启动多个线程,他们分别执行不同的任务。
     简单来说: 一个进程里面有多个线程。线程与线程之间相互独立。
     一般我们都说是多个线程同时执行,但其实处理器在同一时间有且只能接收一个线程,不过多个线程可以交替执行。

2.创建多线程

2.1.继承Thread

优点:
可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率。
Thread: 最正宗的线程类
public class Thread extends Object implements Runnable

Java虚拟机允许应用程序同时执行多个执行线程。 
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。  
1-10  理论上: 数值越大  级别越高  (CPU的随机性)
static class  Thread.State   获得线程的状态
static int MAX_PRIORITY  10
static int MIN_PRIORITY   1  
static int NORM_PRIORITY  5
 
Thread(Runnable target) 
Thread(Runnable target, String name) 
 
static Thread currentThread()  获得当前正在运行的线程
long getId()  默认从1开始
String getName()  获得线程的名字   Thread-0
int getPriority()     获得当前线程优先度   
Thread.State getState()
线程之间调度:
void join()  等待这个线程死亡。 当前线程所有的功能运行完毕(死亡) 其它线程才开始执行
void join(long millis)  
static void sleep(long millis)  当前线程休眠millis 放弃抢占cpu 自动醒过来  继续向下执行
static void yield()  当前线程直接让出抢占cpu的机会  自己又会立马处于就绪状态
    
void setName(String name)     
void setPriority(int newPriority)  
    
void start()  启动线程

线程通信:
(锁对象-->监视器)
void wait()   线程一直等待  死过去了
void wait(long timeout)   在这个时间范围只能一直等待  超出时间  自动醒过来
void notify()  在另外一个线程里面唤醒已经wait的线程
void notifyAll()  在另外一个线程里面唤醒所有的已经wait的线程

2.2 .实现Runnable

3个窗口卖100张票
实现了多线程,但是产生了线程安全的问题。
  :在多线程的环境下,由于线程与线程之间是相互独立,多个线程操作共享的数据 
  参照SaleTicketRunnable.java
  public class SaleTicketRunnable implements Runnable {
	// 重写run方法
	private int ticketNum = 20;

	@Override
	public void run() {
		for (int i = 1; i <= 20; i++) {
			if(ticketNum>0) {
				System.out.println(Thread.currentThread().getName() + "卖了第" + ticketNum + "张票");
				ticketNum--;
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
	}
}

2.3 实现Callable+Future模式

1.Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值
2.Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常

3.线程安全(锁)

synchornized: 同步 (悲观锁,互斥锁)-->有且只有一个线程执行。
锁: 任意一个对象里面都有的监视器(锁)。

3.1.synchornized同步代码块

synchornized(锁对象){
    //有可能出现线程安全的逻辑
}

public class SaleTicketRunnable implements Runnable {
	// 重写run方法
	private  int ticketNum = 20;
	@Override
	public void run() {
		for (int i = 1; i <= 20; i++) {
			synchronized ("abc") {//上锁  对象锁
				if(ticketNum>0) {
					System.out.println(Thread.currentThread().getName() + "卖了第" + ticketNum + "张票");
					ticketNum--;
				}
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}//自动解锁
		}
	}
}

3.2 同步方法

在方法的声明上面,使用synchronized
StringBuffer / Vector

public class SaleTicketRunnable implements Runnable {
	// 重写run方法
	private int ticketNum = 20;

	@Override
	public void run() {// 上锁
		for (int i = 1; i <= 20; i++) {
			saleTicket();
		}
	}// 释放
	private synchronized void saleTicket() {//锁对象?  当前正在运行的类对象 this
		if (ticketNum > 0) {
			System.out.println(Thread.currentThread().getName() + "卖了第" + ticketNum + "张票");
			ticketNum--;
		}
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

3.3手动加锁 Lock-->ReentrantLock

public class SaleTicketCallable implements Callable<String> {
	private int ticketNum = 200;
	private Lock lock = new ReentrantLock();

	@Override
	public String call() throws Exception {
		for (int i = 1; i <= 200; i++) {
			lock.lock();//手动上锁
			
			if (ticketNum > 0) {
				System.out.println(Thread.currentThread().getName() + "卖了第" + ticketNum + "张票");
				ticketNum--;
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//手动释放锁
			lock.unlock();
		}
		return "hello";
	}
}

3.4.死锁

后果: 程序不停,也不继续执行。(阻塞)
产生死锁的几率很低。
1. 多线程的环境下
2. 至少有2个资源
3. 至少有2把锁

手动演示:  嵌套锁+交叉锁
   小明: 手里面有成绩单,想要父亲的零花钱
   父亲:手里面有零花钱, 想要小明的成绩单
   小明/父亲各自都有一把锁  ,锁住自己的资源

public class XiaoMingThread extends Thread{
	@Override
	public void run() {
		synchronized (Locks.XIAOMING_LOCK) {//上锁
			System.out.println("小明有成绩单");
			synchronized (Locks.FATHER_LOCK) {
				System.out.println("小明想要父亲的零花钱");
			}
		}//解锁
	}
}
public class FatherThread extends Thread{
	@Override
	public void run() {
		synchronized (Locks.FATHER_LOCK) {//上锁
			System.out.println("父亲有零花钱");
			synchronized (Locks.XIAOMING_LOCK) {
				System.out.println("父亲想要小明的成绩单");
			}
		}//解锁
	}
}
public class DeadLockTest {
	public static void main(String[] args) {
		XiaoMingThread mingThread = new XiaoMingThread();
		FatherThread fatherThread = new FatherThread();
		mingThread.start();
		fatherThread.start();
	}
}

4.线程通信(等待或唤醒线程)

线程通信:
(锁对象-->监视器)
void wait()   线程一直等待  死过去了
void wait(long timeout)   在这个时间范围只能一直等待  超出时间  自动醒过来
void notify()  在另外一个线程里面唤醒已经wait的线程
void notifyAll()  在另外一个线程里面唤醒所有的已经wait的线程
生产者与消费者模式:(应用场景: 消息队列)
生产者(线程) ---> 缓冲区(池子) --->消费者(线程)

  生产者(线程): 生产数据,存储到缓冲区
  消费者(线程):从缓冲区里面拿数据进行消费。
  缓冲区(池子): 大小有限,30个数据

(MySQL)

1.DDL

1. 创建数据库: 
   create database 数据库名; (名称不可更改)
2. 删除数据库
    drop database 数据库名;(连着表/数据都一块删除,数据无法回滚)
3.创建表
    create table 表名(    tb_user  t_user  user
     字段1 数据类型 [约束],
     字段2 数据类型 [约束],
      ...
     字段n 数据类型 [约束]  
    ); 
  创建学生信息表:  tb_student   id  createtime  updatetime
  create table tb_student(
    sid int(4),
    sname varchar(20),
    gender char(1),
    age  tinyint(2) unsigned,
    score float(4,1),
    birthday date,
    createtime datetime,
    updatetime datetime
  );
  4.表结构 alter(了解)
   4.1 新增新的字段  
       alter table tb_student add age  tinyint(2) unsigned;
   4.2 删除字段
       alter table tb_student drop age;
   4.3 修改字段名称
       alter table tb_student change age sage tinyint(2) unsigned;
   4.4 修改字段的数据类型
       alter table tb_student change sage sage int(2) unsigned;
       alter table tb_student modify sage tinyint(2) unsigned;     
   4.5 修改表名
       alter table tb_student rename to t_student;

2.DML

对表数据进行操作。
1.insert
  1.1 对所有的字段新增数据
      insert into 表名 values (数据1,数据2...数据n),(数据1,数据2...数据n);  ''
 insert into tb_student   values  (1,'jim','M',80.5,'2019-01-01','2019-01-01 12:00:00' ,'2019-01-01 12:00:00',20);
 
 1.2 对指定的字段新增数据(推荐)
    insert into 表名 (字段1,字段2...字段n) values (数据1,数据2...数据n);
    insert into tb_student (sid,sname,gender,score,createtime) values
    (2,'tom','m',100,now());
  1.3 编码处理
     mysql的server: 默认的编码格式 latin  改成utf8
     mysql的核心配置文件: my.ini 
     66 default-character-set=utf8
     100 character-set-server=utf8
     
2.delete
    delete from 表名 [where 条件 ]; 清空表数据   多条记录受影响
    delete from tb_student where sid =1;
    
3.update
   update 表名 set 字段名1 = 值1,字段名2 = 值2 [where 条件];多条记录受影响
   update tb_student set sage = 20 where sname = 'jim1' ;

3.约束

3.1.非空约束notnull (行级约束)

字段必须给值。
create TABLE a(
 id int not null,
 name varchar(20)
);
insert into a values (1,'jim');
insert into a (id) values (2);
Field 'id' doesn't have a default value

3.2.唯一性约束UNIQUE(行级约束)

字段的数据必须是唯一,不可重复的,(除了null)
create table b(
 id int not null UNIQUE,
 name varchar(20) unique
);
insert into b values (4,null);
--  Duplicate entry 'jim' for key 'name

3.3.默认约束default(行级约束)

对某个字段设定默认值。
create table c(
id int not null UNIQUE,
name varchar(20) not null DEFAULT '无名氏',(在not null下只能使用一次)
age TINYINT(2) not null
);

insert into c (id,age) values (2,20);

3.4.主键约束 primary key(行级/表级约束)

第一范式

1NF是对属性的原子性,要求属性具有原子性,不可再分解;

第二范式

2NF是对记录的惟一性,要求记录有惟一标识,即实体的惟一性,即不存在部分依赖;

第三范式

3NF是对字段的冗余性,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖;

张表里面有且只有一个主键。主键列(数据不能为null 必须唯一的)  ==>not null UNIQUE
任意类型的字段(列)都可以充当主键列。(最常用的: int/varchar) ==>id  标识数据的唯一性

如果主键列是整数类型的,一般不会手动给值,都是结合mysql的server自动维护主键列的数据。
会结合使用: auto_increment (默认从1开始,每次自增+1)

修改自增的初始值: alter table 表名 auto_increment = 初始值;
如果主键列是字符串类型,一般不会手动给值,都是结合mysql的server自动维护主键列的数据
create table a(
 id varchar(64) primary key ,
 name varchar(20) not null UNIQUE,
 age tinyint(2)
);
insert into a (id,name,age) values (uuid(),'张三',20);

3.5.外键约束foregin kye(表级约束)

外键不推荐使用,弱化外键。(所有的关联都在逻辑层面进行处理)

一对一: 一个商品一个类型。
tb_type
create table tb_type(
 id int primary key auto_increment,
 typename varchar(20) not null unique,
 desc varchar(100),
 createtime datetime,
 updatetime datetime
);

tb_good
create table tb_good(
 id int primary key auto_increment,
 gname  varchar(20) not null unique,
 price float(10,2) not null,
 createtime datetime,
 updatetime datetime,
 );

怎么表示: 商品都有所属的类型???
alter table tb_good add CONSTRAINT  fk_typeid 
FOREIGN key (typeid ) REFERENCES tb_type (id);
商品有类型。
商品表(从表/子表)typeid 的数据要严格参照类型表(主表) 主键id的数据。
typeid: 外键列(在从表里面)

RESTRICT: 删除,修改都是受限。
CASCADE: 级联操作。 (不能使用)
SET NULL: 前提:外键列可以为null。(可以使用)

表设计

例:

一对一:
(外键列)
一对多:(多对多)
一个老师 vs 多个学生
中间表维护两张表的关系

4.DQL

select * from 表名;  * 通配符  表的所有的字段
指定字段查询:(推荐)
   select uname,rid from tb_user;
语法:
  select 
    指定字段1, 指定字段2
  from 
    [表的list] a,b
  [where 条件1 or/and 条件2] 
  [group by 字段 分组查询]
  [having 条件1] 对分组之后的数据进行过滤
  [order by 字段] 排序
  [limit ?/?,?] 对记录的限定

4.1.条件查询

1.1 where语句
--  查询学号为S_1001,或者姓名为liSi的记录
select * from stu where sid = 's_1001' or sname = 'LISI';
-- 2.4 查询学号为S_1001,S_1002,S_1003的记录
select * from stu where sid = 's_1001' or sid = 's_1002' or sid = 's_1003';
-- select * from stu where sid  in ('s_1001','s_1002','s_1003');
-- 2.5 查询学号不是S_1001,S_1002,S_1003的记录
select * from stu where sid not in ('s_1001','s_1002','s_1003');
select * from stu where sid != 's_1001' and sid != 's_1002' and sid = 's_1003';
-- 2.6 查询年龄为null的记录
select * from stu where age is null;
-- 2.7 查询年龄在20到40之间的学生记录
select * from stu where age>=20 and age <=40;
select * from stu where age BETWEEN 20 and 40;
-- 2.8 查询性别非男的学生记录
 select * from stu where gender ='female' or gender is null;
-- 2.9 查询姓名不为null的学生记录
select * from stu where  age is not null;

4.2.模糊查询(like)

'%a'     //以a结尾的数据
'a%'     //以a开头的数据
'%a%'    //含有a的数据
'_a_'    //三位且中间字母是a的
'_a'     //两位且结尾字母是a的
'a_'     //两位且开头字母是a的
1. 淘宝 (模糊匹配)  
-- 3.3 查询姓名以“z”开头的学生记录  % 
select * from stu  where sname like 'z%';
-- 3.5 查询姓名中包含“a”字母的学生记录
select * from stu  where sname like '%a%';

4.3.字段控制查询

3.1 distinct  去除重复数据(只针对某个字段而言)
-- 查询学生表里面性别(去除重复的数据 )
select DISTINCT gender from stu;

3.2 字段数据NULL判断
-- 查询员工的薪水和薪金之和
-- 字段之间是可以相互运算(数值类型)
-- 任何数据与null进行运算 最后的结果都是null
-- ifnull(字段,设定的数据)
-- ifnull(comm,0) 如果comm是null,用0进行替换,否则还是本身的数据。
select sal,comm,(sal+ifnull(comm,0)) from emp;

3.3 起别名查询 [as]
select e.sal s,e.comm c,(sal+ifnull(comm,0)) '薪水和佣金之和' from emp e;

4.4.排序

order by 字段1,字段2 根据某个/些字段按照升序(asc)/降序(desc)排列  默认是按照升序排列
-- 5.2 查询所有学生记录,按年龄降序排序  年龄一致的话  根据id进行降序排列
select * from stu ORDER BY age desc,sid desc;

4.5.分组查询

group by 字段
分组函数/聚合函数:
   count(id): 根据某个字段统计表记录数(排除null)
   max/min/avg(平均)/sum(字段)
-- 查询每个部门的部门编号和每个部门的工资和:
 select  deptno,sum(sal) from emp GROUP BY deptno;
-- 查询每个部门的部门编号以及每个部门的人数:
select  deptno ,count(*) from emp GROUP BY deptno;
-- 查询每个部门的部门编号以及每个部门员工工资大于1500的人数:
 select  deptno ,count(*) from emp where sal>1500 GROUP BY deptno;
 select  deptno ,count(*) from emp  GROUP BY deptno ;-- 临时表
 
 having:分组之后的数据进行过滤
 -- 查询工资总和大于9000的部门编号以及工资和:
 select  deptno,sum(sal)  from emp GROUP BY deptno having sum(sal)>9000; 
 
 where vs having:
   1.都是过滤不符合条件的数据
   -- 查询薪资大于1500
    select * from emp having sal>1500;
    select * from emp where sal>1500;
   2. where不能与分组函数结合使用  having可以
   3. where必须使用在GROUP BY之前  having使用在GROUP BY之后

4.6.关联查询

查询的记录涉及多张表。
1. 等值连接:连接的条件是一个等号
案例:查询员工信息,要求显示员工号,姓名,月薪,部门名称 
select  e.empno,e.ename,e.sal,d.dname,d.loc from emp e,dept d where e.deptno = d.deptno;

2.不等值连接:连接条件不是等号
案例:查询员工信息,要求显示:员工号,姓名,月薪,薪水的级别
select e.empno,e.ename,e.sal,s.GRADE from emp e,salgrade s WHERE e.sal BETWEEN s.LowSAL and s.HISAL ORDER BY sal;

3.外连接
内连接 inner join on/where ==>等值连接
select d.deptno,d.dname,count(*) from emp e INNER JOIN dept d on  d.deptno = e.deptno GROUP BY deptno
左外连接 left join on  以左表为基准  右表没有的数据用0/Null进行填充
右外连接 right join on 以右表为基准 左表没有的数据用0/Null进行填充
按部门统计员工数,部门号,部门名称,人数
select d.deptno,d.dname,count(empno) from  emp e RIGHT JOIN dept d on d.deptno = e.deptno GROUP BY deptno;
select d.deptno,d.dname,count(empno) from  dept d left JOIN emp e on d.deptno = e.deptno GROUP BY deptno;

4.自连接(把自己看成多张表进行关联)
查询员工姓名和员工的老板的名称
select e1.* , e2.* from emp e1 left join emp e2 on e1.mgr = e2.empno ORDER BY e1.empno;

5.子查询
查询薪资最高的员工的信息
select * from emp where sal =(select max(sal) from emp); 
查询工资为20号部门平均工资的员工信息.
select * from emp where sal = (select avg(sal) from emp where deptno = 20);

6.集合查询(了解)
unoin / union all
系统平台,用户量比较大,(mycat分库分表) t_user1  t_user2 都是存储的用户的信息。
通过一条select语句,查询所有的用户的信息。
多次查询的结果合并在一起  union  / union all
union 去除重复的数据(行记录)
distinct(单个字段) 
select * from t_user1
union 
select * from t_user2;
7.分页查询 limit 对最后的结果进行限定(mysql独有)
 每一页: size 固定的 20
 总页数: 总记录数select count(*) from 表 /size
 105/20 = 6
(在任意select语句,排序,分页查询都是必不可少的。)
查询第一页的数据
limit size; 指定从第一条记录开始查询  查size条
limit ?,size  指定从第n条记录(index)开始查询  查size条
第1页 currentPage = 1
select  * from emp limit 5 ;
select  * from emp limit 0,5;
第2页 currentPage = 2
select  * from emp limit 5,5;
第3页 currentPage = 3
select  * from emp limit 10,5;

5.函数

大全↓↓↓

点击打开

(JDBC)

连接MySQL

维护连接唯一性

JDBC: java database connectivity > java数据库连接技术。
java开发人员
>码代码 通过代码去操作指定数据库表数据的。
jdk: 一组api(接口)
DriverManger:管理数据库服务(一般用于注册驱动,获得连接操作)
Connection: 代表与特定数据库的连接。

1.连接数据库

mysql:
   1. 创建一个连接
    1.用户名 username = root
    2.密码 password = root
    3.数据库服务所在位置  url = jdbc:mysql://ip地址:3306/数据库名称
       jdbc:mysql://localhost:3306/java
    4.依赖于指定数据库的驱动(类库文件/jar文件) driver = mysql的驱动(下载)
      与mysql的服务版本一致。
      https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.47/ 
public static void main(String[] args) {
		// 1.准备基本数据
		String username = "root";
		String password = "root";
		String url = "jdbc:mysql://127.0.0.1:3306/java";
		String driver = "com.mysql.jdbc.Driver"; // java.sql.Driver
		// 2.获得数据库连接
		try {
			// 2.1 注册驱动
			Class.forName(driver);
			// 2.2 获得连接 DriverManager.
			Connection connection = DriverManager.getConnection(url, username, password);
			System.out.println("mysql的连接:"+connection);//与特定数据库的连接
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
1.基本的信息 保存到配置文件 jdbc.properties
  加载一次配置文件的内容,
  
2.管理连接-->保证对象的唯一性
private static Connection conn;
	static {
		try {
			Class.forName(PropUtil.getProp("jdbc.driver"));
			conn = DriverManager.getConnection(PropUtil.getProp("jdbc.url"), PropUtil.getProp("jdbc.username"), PropUtil.getProp("jdbc.pass"));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}
	
	public static Connection getConn() {
		return conn;
	}

------------------------------------------------------------------------------------
    //ThreadLocal<T> 类    在一个线程里面保证T的唯一性 (有且只有一个T的实例)
	//使用ThreadLocal<T>去维护Connection的对象
	//把连接对象存储到ThreadLocal里面(就存储一个元素)
	private static final ThreadLocal<Connection> CONN_LOCAL = new ThreadLocal<>();
	/**
	 * 获得数据库连接
	 * @return
	 */
	public static Connection getMysqlConn() {
		// 加载核心配置文件
		Connection connection = CONN_LOCAL.get();//获得ThreadLocal里面存储的Connection的对象
		if(connection == null ) {//第一次获得连接的对象
			try {
				Class.forName(PropUtil.getProp("jdbc.driver"));
				connection = DriverManager.getConnection(PropUtil.getProp("jdbc.url"), PropUtil.getProp("jdbc.username"), PropUtil.getProp("jdbc.pass"));
				//获得真正的连接  
				CONN_LOCAL.set(connection);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return connection;
	}

2.操作表

详细增删改查见学生查询系统

3.事务

数据库服务执行的一系列单元(DML语句insert delete update)。--->要么全部成功,要么全部失败。
数据库四大特性:ACID
原子性(Atomicity)
​ 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
​ 事务前后数据的完整性必须保持一致。
隔离性(Isolation)
​ 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
​ 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响.

有4种隔离级别机制用来维护数据库事务:
四种隔离级别: 通过select @@tx_isolation可以查看。

1. 读未提交(不可重复读)   read uncommitted
 老板给小明发奖金  发了20000 但是事务并没有提交  小明查看奖金  一看到账20000,开心坏了。老板一看数额不对,应该是发2000,赶紧修改金额,提交事务,这时候小明再去查看奖金,发现卡里变成2000
 这里就出现脏读,并发的两个事务:事务1 领导发奖金    事务2小明查看奖金   事务2读取到了事务1未提交的内容
           
2. 读已提交(sqlserver  oracle的默认级别)  read  committed
 小明查看卡里有2000 准备去取钱   小明老婆正好也在转账  把小明的2000转到另外一个账户上 并在小明取钱之前提交了事务  
 当小明取钱时发现取钱失败  余额不足  小明非常纳闷  明明查看有2000 却没有取钱成功。。,
 这里也是出现了脏读  并发的两个事务:事务1 小明取钱    事务2小明老婆转账    事务1先读取了数据   事务2紧接着更新了数据并提交事务  当事务1再次读取数据的时候 数据已经发生了变化。
  
 3. 可重复读  repeatable read(mysql默认的机制)
 小明卡里2000  要取钱  一旦开始读取卡里余额的时候 事务已经开始   
 小明老婆就不能对卡里数据进行修改   也就是不能转账	
 
 4. 可串行化(脏读) Serizable
 最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用

4.泛型

集合:List Map<K,V>=>(编译)限定集合元素的数据类型的 (程序运行(jvm add put-> Class) 泛型擦除)
泛型的标志: <T,L,A> E T K v 参数化数据类型
>类型自动转换
泛型的作用:
1.修饰类 泛型类
2.修饰接口 泛型接口
3.修饰方法 泛型方法(类)

5.枚举类

class
interface   
    
单例模式: 类  在一个线程里面  有且只有一个对象
enum:多例。在一个线程里面  有n个不变的对象

public enum 枚举类类名{
   对象1(1),
   对象2(1,"abc"),
   对象3,
   ....
   对象n;
   
   //成员变量
   //成员方法
   //private构造
}

6.service

dao: data access Object 数据访问对象 (数据库持久层)-->CURD
service: 业务逻辑处理层(灵活)--->1.校验数据合理性  2.文件上传  ....

在service里面调用dao
@Override
	public Map<String, Object> selectByPage(int page) {
		studentDao = new StudentDaoImpl();
		int totalCount = studentDao.selectStudentCount();

		// page 1-总页数
		// totalPage = 总记录数(select count(sid) from tb_student)/size
		int totalPage = (totalCount % 5 == 0) ? (totalCount / 5) : (totalCount / 5 + 1);
		if (page < 1) {
			// 用户看到的就是第一页的数据
			page = 1;
		} else if (page > totalPage) {
			// 用户看totalPage的数据
			page = totalPage;
		}
		List<Student> stuList = studentDao.selectStudentByPage(page);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("msg", "分页查询成功");
		map.put("data", stuList);
		return map;
	}

6.2.自增id

新增一条老师记录:
   并且需要在中间表关联相关学生的信息 (指定老师有的学生)
    @Override
	public void addTeacher(Teacher teacher, int[] stuIds) {
		conn = DBUtil.getMysqlConn();
		String sql1 = "insert into tb_teacher (name,age,createtime) values (?,?,now())";
		String sql2 = "insert into tb_teacher_student (tid,sid) values (?,?)";
		// 获得自增的主键列的数据
		// 当将sql发送到mysql的server里面的时候 要告诉数据库 要返回自增的id值
		// select last_insert_id()
		try {
			ps = conn.prepareStatement(sql1, Statement.RETURN_GENERATED_KEYS);
			ps.setObject(1, teacher.getName());
			ps.setObject(2, teacher.getAge());
			ps.executeUpdate();// 执行完insert之后 获得自增主键id值了

			rs = ps.getGeneratedKeys();
			long key = 0;
			if (rs.next()) {
				key = rs.getLong(1);//获得新增的id值
			}
            ....
        }
posted @ 2020-05-25 15:41  Amier_X  阅读(238)  评论(0编辑  收藏  举报