JAVA基础整理

1990年sun启动绿色计划,1994年gosling参加硅谷大会,演示java成功。1995年sun正式发布java第一个版本。
Java se 桌面开发(管理系统)->基础中的基础
Java ee web开发
Java me 手机开发
Jdk:全称是:Java Development Kit 中文是Java开发工具包。(jre,java的工具,如编译器,类库)。
Jre:java运行环境
Java的8种基本数据类型byte,short,int,long,float,double,char,Boolean.

计算机中数都是以二进制储存的,最高位为符号位,0表示正数,1表示负数
整数类型取值范围

这里写图片描述
浮点类型包括float类型和double类型,其中double类型的精度是float的两倍。
Char用于表示单一的字符,底层实现也是数字类型,所以它可以和int类型之间进行相互转化。
这里写图片描述

能进行隐含转换的只能是常量而不能是变量。
数据类型可以自动的从低精度到高精度转化。

Byte<short<int<long<float<double

在java中,对char进行运算的时候,直接当做ascii码对应的整数对待。
汉子是用unicode码进行存储的。
标识符:
1. 英文字母是大写的A~Z,小写的是a~z,以及“_”,“$”。
2. 数字包括0~9.
3. 其他的符号不能用在标识符里。
4. 不能用Java所保留的关键字。
5. Java标识符是大小写敏感的。

标识符要以字母,或“_”,或“$”开头。

逻辑运算符:
&& 与
| | 或
! 非

Switch(条件表达式)
条件表达式数据类型,应和case后的常量类型一致
Case后的类型为byte,short ,int ,char , enum(枚举)。

For循环执行顺序

这里写图片描述
定义对象与声明对象

public static void main(String args[]){

        Person a=new Person();      //定义一个对象a,并分配一个存储空间
        a.age=10;
        a.name="小明";
        Person b;       //声明对象b,没有分配存储空间
        b=a;        //b指向a的存储空间地址,公用一个空间,b对空间的修改将影响到a
        Person c;   //同b c对空间的修改将影响到b,与a
        c=b;
        System.out.println(c.age);
    }

类名字首字母要大写,方法名要小写
Equals 比较内容是否相等,区分大小写
EqualsIgnoreCase() 比较内容是否相等,不区分大小写

图片 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
这里写图片描述

JVM由图上可以看出分三部分,栈区是存放引用的,堆区是存放构件起来的对象的。

String a=”abc”;
String b=”abc”;
他们都是存放在堆区的,他们有一块内存,内容(equals)相等,内存首地址地址(= =)也相等,因为他们在同一块内存中。
String k=new String(“abc”);
String j=new String(“abc”);
他们的引用存放在栈区,内容存放在堆区,但是会分别存放在不同的内存中,他们的内存地址是不相同的,但是内容(abc)虽然在不同的内存中,还是相等的。

多行注释
Ctrl + /

用子类构造方法调用父类的构造方法的一个例子:
用Super();方法就可以办到

package com.stu;

public class ExtendsS {


    public static void main(String[] args) {

        Son2 s1=new Son2();
    }

}

class Base{
    public Base(){
        System.out.println("ok");
    }
}
class Son2 extends Base{
    public Son2(){
        super();
    }

}

上面的代码会输出 ok

这里写图片描述
类的成员变量可以不初始化 如 :int a;
但是如果是局部变量,例如写在 main函数里的,需要初始化。

Java中的命名法
1. 驼峰表示法
myCry
2. 下划线法
ji_suan

构造方法
构造方法是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。
① 方法名和类名相同,首字母大写。
② 没有返回值
③ 在创建一个类的新对象时,系统会自动的调用该类的构造方法完成对新对象的初始化。
④ 构造方法的主要作用是初始化你的成员变量。

This
This 是属于一个对象的,而不是属于一个类的。
This不能在类定义的外部使用,只能在类定义的方法中使用。

类变量
类变量是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象修改它时,修改的也是同一个变量。
定义类变量:
访问修饰符 static 数据类型 变量名;

Static int total=0;
Total是静态变量可以被任何一个对象访问。
类也可以直接访问静态变量 类名.静态变量名

创建对象后就会执行构造函数

Static{
i++;
}
静态区域块 只会执行一次,假如一个对象都不创建也会执行此静态区域块
但是构造函数是不会执行的。

类方法
类方法是属于所有对象实例的,可以节省内存的开销,类变量原则上用类方法去访问

访问修饰符 static 数据返回类型 方法名(){}

加上static称为类变量或静态变量,否则称为实例变量
类变量是与类相关的,公共的属性
实例变量属于每个对象个体的属性

类方法中不能直接访问非静态变量(类变量)
普通的成员方法可以访问静态变量(类变量)
同样,在静态方法中也不能直接调用非静态方法(类方法)
例如

public static void main(String args[]){

        Stu stu1=new Stu(21,"花花",200);  
        Stu stu2=new Stu(22,"白白",220);
        System.out.println(Stu.total()); //此处假如total不是静态方法,则不能访问,会提示无法从静态上下文中引用非静态方法
}
}

例如

class Stu{

    int age;
    String name;
    int fee;
    static int totalFee;
    public Stu(int age,String name,int fee){

        this.age=age;
        this.name=name;
        totalFee+=fee;
    }
    public static int total(){

    age++; //此处是错误的,类方法不能直接访问非静态变量
    return totalFee;
    }

}

类也可以直接访问类方法 类名.类方法名

抽象
我们在前面去定义一个类的时候,实际上就是把一类事物的共有的属性和行为提取出来,形成一个物理模型(模板),这种研究问题的方法称为抽象。
封装
封装就是把抽象出来的数据和对数据的操作封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(成员方法),才能对数据进行操作。

Java中的控制修饰符
这里写图片描述


小写字母 用点隔开分层
打包命令:package 包名;
引入包:import 包名;
Java.lang.* 包 自动引入
Java.util.* 工具包
Java.net.* 网络开发包
Java.awt.* 窗口工具包

继承
继承可以解决代码重用,让我们编程更加靠近人类的思维,当多个类存在相同的属性和方法是,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends语句来声明继承父类;
Class 系类 extends 父类
这样子类就会自动拥有父类定义的某些属性和方法。

注意:
① 子类最多只能继承一个父类(指直接继承)
② Java中所有的类都是Object类的子类

方法的重载(overload)
方法重载就是类的同一种功能的多种实现方式,到底采用哪种方式,取决于调用者给出的参数 例如:

对象名.getMax();会根据括号内的类型自动调用哪个函数
Public int getMax(int i,int j)
Public float getMax(float a,float b)
注意事项:
① 方法名相同
② 方法的参数类型,个数,顺序至少有一个不同
③ 方法返回类型可以不同(如果只是返回类型不一样,则不可以构成重载)
④ 方法的修饰符可以不同(如果只是修饰符不一样,则不可以构成重载)
方法覆盖、方法重写(override)
方法覆盖就是子类有一个方法,和父类的某个方法的名称,返回类型,参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法。
可以解决子类和父类方法的异同
注意:
① 子类的方法的返回类型,参数,方法名称,要和父类方法的返回类型,参数,方法名称完全一样,否则编译出错。
② 子类方法不能缩小父类方法的访问权限。

多态
就是指一个引用(类型)在不同情况下的多种状态。也可以这样理解:多态是指通过指向父类的指针,来调用在不同子类中实现的方法。

注意:
① java允许父类的引用变量引用它的子类的实例(对象)
例如:Animal animal=new Cat(); 这种转换是自动完成的

一个多态的例子:

public class Test2 {

    public static void main(String[] args) {

        Master master1=new Master();
        master1.feed(new Cat(),new Fish());

    }   
}
class Master{

    public void feed(Animal an,Food f){
        an.cry();
        f.showName();

    }   
}
class Food{
    String name;

    public void showName(){

        System.out.println("不知道叫什么食物");
    }   
}
class Fish extends Food{

    public void showName(){

        System.out.println("鱼");
    }
}
class Bone extends Food{

    public void showName(){

        System.out.println("骨头");
    }
}
class Animal{
    int age;
    String name;

    public void cry(){

        System.out.print("不知道怎么叫");
    }

}
class Cat extends Animal{

    public void cry(){
            System.out.print("猫爱吃");
    }
}
class Dog extends Animal{

    public void cry(){

        System.out.println("狗爱吃");
    }
}

抽象类
当父类的一些方法不能确定时,
可以用abstract关键字来修饰该方法(就是抽象方法),抽象方法是不能有方法体的。只能在他的子类里面去实现。
用Abstract来修饰该类(就是抽象类)。
当一个类继承的父类是抽象类的话,需要把抽象类中的所有的抽象方法全部实现

例如:

Abstract class Animal{  //抽象类

    String name;
    Int age;
    Abstract public void cry();  //抽象方法,此处的抽象方法不能有方法体。
}
Class Cat extends Animal{
    Public void cry(){
        System.out.println(“抽象类的方法在子类里面实现”);
}
}

注意事项:
① 抽象类不能实例化
例如上例中
Animal an = new Animal(); //这样是错误的,不能实例话抽象类,不能建立抽象类的对象
② 抽象类可以没有抽象方法,也就是被abstract修饰的方法。
③ 一旦类包含了abstract方法,则这个类必须声明为抽象类
④ 抽象方法不能有方法体,非抽象方法可以有方法体。

接口
接口就是给出一些没有内容的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
定义接口:
Interface 接口名{
变量;
方法;
}
语法:
Class 类名 implements 接口{
方法;
变量;
}
接口是更加抽象的抽象类,抽象类里面的非抽象方法可以有方法方法体,接口里面的所有方法都没有方法体。接口体现了程序设计的多态和高内聚低耦合的设计思想

当一个类实现了接口就要求该类把这个接口的所有方法全部实现。

一个接口的例子:

package com.stu;

public class Interfac {

    public static void main(String[] args) {

        Computer computer1=new Computer();
        Camora camora1=new Camora();
        Phone phone1=new Phone();
        computer1.useUsb(camora1);
        computer1.useUsb(phone1);
    }
}
interface Usb{

    public void start();
    public void stop();
}
class Camora implements Usb{

    public void start(){
        System.out.println("我是相机,我开始工作了");
    }
    public void stop(){
        System.out.println("我是相机,我停止工作了");
    }
}
class Phone implements Usb{
    public void start(){
        System.out.println("我是手机,我开始工作了");      
    }
    public void stop(){
        System.out.println("我是手机,我停止工作了");
    }   
}
class Computer{

    public void useUsb(Usb usb){
        usb.start();
        usb.stop();

    }}

注意事项:
① 接口不能被实例化
Usb usb =new Usb(); // 是错误的,实例化接口了
② 接口中的所有方法都不能有主体。
③一个类可以实现多个接口。
例如:
Class Camere implements Usb,Kkk{
}
⑤ 接口中可以有变量(但是变量不能用private和protected修饰)
接口中的变量必须初始化。 而且只能用public ,static , final修饰
接口中的变量本质上都是static 的而且是final类型的,不管你加不加static修饰
在开发中,我们把经常用的变量定义在接口中,作为全局变量使用。
访问形式: 接口名.变量名
④ 一个接口不能继承其他的类,但是可以继承别的接口。

继承和实现是可以同时发生的
例如:
Class 类名 extends 父类 implements 接口名{
}

接口的好处:
① 实现接口可以看作是对继承的一种补充
② 实现接口可在不打破继承关系的前提下,对某个类功能扩展,非常灵活。

Final
Final可以修饰变量或者方法
① 当不希望父类的某个方法被子类覆盖时,可以用final关键字修饰
② 当不希望类的某个变量的值被修改,可以用final修饰,如果一个变量时final的则必须初始化
③ 当不希望类被继承时,可以用final修饰。

Final  Class Send  //用 final修饰,则不可以被继承
{
    Final int a=0 ;  // 用final修饰,则表示不可以被下面的子类修改
Final public void sendMes(){  // 用final修饰,则表示不可以被修改
}
}

注意事项
① 用final修饰的变量又叫常量,用xx_xx_xx来命名。
② Final修饰的变量在定义时,必须赋值,并且以后不能再赋值。

数组
数组的定义:
数组类型 数组名[ ]=new 数组类型[ 大小 ]
Int a [ ]=new int [5 ];
数组的用法:
第一步先声明
数据类型 数组名[ ]; 也可以 数据类型[ ] 数组名;
Int a [ ]; int [ ] a;
第二步:创建数组
数组名=new 数组类型[ 大小 ]
a =new int [ 10 ] ;
第三步:数组的引用
数组名[下标]
a [ 7 ]
看下面这种定义方式,这样也是可以的
int arr[]=new int[]{1,23,3}; 坑爹啊。。。

查看数组的大小
数组名.length
数组大小必须事先指定
定义同时初始化数组
语法:
数据类型 数组名[ ]={元素值,元素值。。。}
Int a[ ]={2,5,6,7,8}

对象数组
对象数组的一个例子:

public class Arr {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Dogs dog[]=new Dogs[5];
        dog[0]=new Dogs();  //注意此处必须NEW一下
        dog[0].setAge(10);
        dog[0].setName("huahua");
        System.out.println(dog[0].getName());
        System.out.println(dog[0].getAge());

    }
}
class Dogs{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

}

排序:
找出体重最大的小狗(选择排序法)
核心代码:

Float maxWeight=dogs[0].getWeight();
Int maxIndex=0;
For(int i=1;i<dogs.length;i++){
    If(maxWeight<dogs[i].getWeight()){
        maxWeight=dogs[i].getWeight();
        maxIndex=I;
} 
}

排序时将一群数据,依指定的顺序进行排列的过程。
内部排序
指将需要处理的所有数据都加载到内存的存储器中进行排序,包括交换式排序法,选择式排序法,插入式排序法;
外部排序法
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序,包括合并排序法,和直接合并排序法

交换式排序法
1冒泡排序法bubble sort
2快速排序法 quick sort

冒泡排序 一个案例
这里写图片描述
由小到大代码如下

public class BubbleSort {

     */
    public static void main(String[] args) {
        int arr[]={2,10,3,33,-1,32};
        int temp=0;
        for(int i=0;i<arr.length-1;i++){//n个数进行冒泡排序 外
                                             //层循环需要进行n-1次

for(int j=0;j<arr.length-1-i;j++){

                if(arr[j]>arr[j+1]){
                    temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }

            }
        }
        for(int i=0;i<arr.length;i++){

            System.out.println(arr[i]);

        }
    }
}

选择排序法
选择排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,经过和其他元素重整,再依原则交换位置后达到排序的目的。
基本思想:
第一次从1-n个数中选取最小值,与第1个数交换,第二次从2-n个数中选取最小值,与第2个数进行交换,第三次从3-n个数总选取最小值与第3个数进行交换以此类推。。。
一个案例:
这里写图片描述

代码如下:

public class SelectSort {

    public static void main(String[] args) {
        Select s1=new Select();
        int arr[]={2,55,43,34,56,1,23,4};
        s1.select(arr);
        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }
    }
}
class Select{

    public void select(int arr[]){
        int temp=0;
        for(int i=0;i<arr.length-1;i++){
            int min=arr[i];
            int minIndex=i;
            for(int j=i+1;j<arr.length;j++){
                if(min>arr[j]){
                    min=arr[j];
                    minIndex=j;     
                }
            }
            temp=arr[i];
            arr[i]=arr[minIndex];
            arr[minIndex]=temp;     
        }
    }
}

插入排序法
插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。
基本思想:把N个待排序的元素看成一个有序表和一个无序表,开始时有序表只包含一个元素,无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表、

一个案例:

这里写图片描述
代码如下:

public class InsertSort {


    public static void main(String[] args) {

        int len=10;
        int arr[]=new int[len];
        for(int i=0;i<len;i++){

            int ran=(int)(Math.random()*100);
            arr[i]=ran;
        }

        Insert in=new Insert();
        in.sort(arr);
        for(int i=0;i<arr.length;i++){

            System.out.print(arr[i]+" ");
        }

    }
}
class Insert{

    public void sort(int arr[]){

        for(int i=1;i<arr.length;i++){

            int insertVal=arr[i];
            int index=i-1;
            while(index>=0&&insertVal<arr[index]){

                arr[index+1]=arr[index];
                index--;
            }
            arr[index+1]=insertVal;
        }
    }
}

快速排序法
是对冒泡排序的一种改进。
基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

查找
顺序查找
二分查找
前提是要查找的数据是有序的
一个案例
代码如下

package com.stu;

public class Binfind {

    public static void main(String[] args) {
        BinaryFind b1=new BinaryFind();
        int arr[]={2,3,5,6,7,12,34,56,88,99};
        b1.find(0, arr.length-1, arr, 56);
    }

}
class BinaryFind{

    public void find(int leftIndex,int rightIndex,int arr[],int val){

        int midIndex=(leftIndex+rightIndex)/2;
        int midVal=arr[midIndex];
        if(rightIndex>leftIndex){
            if(val<midVal){

                find(leftIndex,midIndex-1,arr,val);

            }
            else if(val>midVal){

                find(midIndex+1,rightIndex,arr,val);

            }
            else if(val==midVal){

                System.out.println("找到了,下标是"+midIndex);
            }

        }
    }   
}

多维数组
二维数组
定义:

类型 数组名[ ][ ]=new 类型[大小] [大小];
比如: int a[ ] [ ] =new int[2][3];

一个案例 打印下面图形

0 0 0 0 0 0
0 0 1 0 0 0
0 2 0 3 0 0
0 0 0 0 0 0

代码如下:

public class Array {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
            int a[][]=new int[4][6];
            a[1][2]=1;
            a[2][1]=2;
            a[2][3]=3;
            for(int i=0;i<4;i++){
                for(int j=0;j<6;j++){

                    System.out.print(a[i][j]+" ");
                }

                System.out.println();
            }
    }
}

二进制,位运算,移位运算
二进制(binary)
二进制是逢2进位的进位制,0,1是基本算符。
现代的电子计算机技术全部采用的是二进制,因为它只使用0,1两个数字符号,非常简单方便,易于用电子方式实现。计算机内部处理的信息,都是采用二进制数来表示的。

一个字节是8位,在计算机里面存储时最高位是符号位,只有0和1两个符号,0代表正数,1代表是负数。

下面是一个案例,存放正数1
这里写图片描述

对于有符号的数而言
① 二进制的最高位是符号位,0表示正数,1表示负数
② 正数的原码,反码,补码都是一样的。
③ 负数的反码=它的原码符号位不变,其它位取反
④ 负数的补码=它的反码+1
⑤ 0的反码,补码都是0
⑥ Java没有无符号数,换言之,java中的数都是有符号的
⑦ 在计算机运算的时候,都是以补码的方式来运算的。

位运算符

& 按位与  两位全为1,结果为1
|  按位或  两位有一个为1,结果为1
^ 按位异或  两位一个为0,一个为1,结果为1
~ 按位取反 0-> 1 1->0
public class Wei {

    public static void main(String[] args) {
        System.out.println("~2="+(~2));

    }
}

输出 -3

移位运算

>> 算数右移  低位溢出,符号位不变,并用符号位补溢出的高位  
<< 算数左移  符号位不变,低位补0
>>> 逻辑右移 低位溢出,高位补0

例如: 1>>2=0
把1 算数右移两位
不论是位运算还是移位运算都需要转换成补码再进行运算
0 0000000000000000000000000000001
向右西东两位
0 00000000000000000000000000000001
移到后面的2位就不要了 中间空出来的用符号位的数补充

集合框架
集合类 重要作用,增删改查,修改,显示
这里写图片描述

List结构的集合类(列表)存储的元素有顺序,可重复
ArrayList, linkedList , Vector , Stack

Map结构的集合类
HashMap, Hashtable

Set结构的集合类
HashSet, TreeSet

Queue结构的集合类
Queue 接口
集合类ArrayList (数组列表)
每次添加的数据如果不指定位置都会放在尾部
ArrayList是由数组实现的List列表,它提供常量事件的位置访问,而且速度很快。
可以放入同样的对象,如a1.add(c1);a1里面存放的是c1的引用,然后指向相同的内存地址,并不会把原来c1覆盖掉
一个增加数据的案例
代码如下

import java.util.ArrayList;
class Arraylist {

    public static void main(String[] args) {

        ArrayList a1=new ArrayList();
        System.out.println(a1.size());
        Clerk c1=new Clerk("宋江",22,1200);
        Clerk c2=new Clerk("吴用",23,1300);
        Clerk c3=new Clerk("林冲",24,1400);
        a1.add(c1);
        a1.add(c2);
        a1.add(c3);
a1.add(c1);  //可以放入同样的对象
        System.out.println(a1.size());
        for(int i=0;i<a1.size();i++){     // 进行遍历

            Clerk tm=(Clerk)a1.get(i);

            System.out.println("名字是+"+tm.getName());
        }
    }
}
class Clerk{
    String name;
    int age;
    int sal;
    public Clerk(String name,int age,int sal){
        this.name=name;
        this.age=age;
        this.sal=sal;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

从 a1中删除 一个对象的代码
A1.remove(1);

LinkedList(链表列表)
其内部是依赖双链表来实现的,因此具有很好的插入删除功能,但随即访问元素的性能相对较差,适合用在在插入,删除多,元素随即访问少的场合。没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。
LinkedList ll=new LinkedList();
Add()方法
addFirst()方法,加在链表的最前面
addLast() 方法,加在链表的尾部

Vector(向量)
Vector vv=new Vector();
与ArraryList基本相同,不同之处在于该方法是同步的
Stack(栈)
Add(); 方法 默认加在前面;
Stack stack=new Stack();

这里写图片描述

MAP
HashMap
是键值对的形式存放对象的,如果放了相同的键(键也是object类型的),会出现覆盖原来存放的对象。存放的对象是没有顺序的。
HashMap hm=new HashMap()
Emp emp=new Emp(“s001”,”aaa”,3.45f);
Hm.put(“s001”,emp); //用这个方法添加对象,前面是键,后面是对象。

查找函数

if(Hm.containsKey(“s001”)) {
    System.out.println(“有该员工”);
    Emp emp=(Emp)hm.get(“s001”); //可以取到该对象;
    System.out.println(“名字是=”+emp.getName());  //输出名字
}

//遍历HashMap中所有的KEY,VALUE
用一个迭代器 iterator

一个案例

package com.manager;
import java.util.*;
public class HashM {

    public static void main(String[] args) {

        Emp emp1=new Emp("花花","s001",44.3f);
        Emp emp2=new Emp("白白","s002",33.3f);
        Emp emp3=new Emp("嘿嘿","s003",22.3f);
        HashMap hm=new HashMap();
        hm.put("s001", emp1);
        hm.put("s002", emp2);
        hm.put("s003", emp3);
        //遍历HashMap中的所有Key和value
        //迭代器 Iterator 
        Iterator it=hm.keySet().iterator();
 //keySet(),取出存放的KEY值
        while(it.hasNext()){
            //取出key值
            String key=it.next().toString(); 
 //next()返回迭代的下一元素取出存放的KEY值
            //通过KEY取出VALUE
            Emp emp=(Emp)hm.get(key);       
//get()通过输入的KEY值,返回所包含的所有内容
            System.out.println("名字是"+emp.getName());
        }

    }
}

Hashtable
Hashtable 和HashMap 在用法上基本一样

Hashtable ht=new Hashtable();
这里写图片描述

总结的几点建议
① 如果要求线程安全,使用Vector,Hashtable
② 如果不要求线程安全,应使用ArrayList,LinkedList,HashMap
③ 如果要求键值对,则使用HashMap,Hashtable
④ 如果数据量很大,又要线程安全则考虑Vector

上面ArrayList, linkedList , Vector , Stack 都是继承了List接口,HashMap, Hashtable
都是继承了MAP接口,所以定义的时候可以写成下面的形式
List l1=new ArrayList();
Map hm=new HashMap();

Set
Set接口与List接口最大的区别在于Set中没有重复的元素。Set中的对象没有特定的顺序。
Sorted接口具有排序的功能,TreeSet类则是实现了该接口;HashSet类使用了哈希算法存取集合中的元素,存取速度比较快。
TreeSet
TreeSet类是实现了接口SortedSet 的类,是这个接口的唯一实现方式,当需要按值排序迭代的时候就需要使用TreeSet。此类保证排序后的set按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序进行排序。
Hashset
元素在其中存储不保证任何顺序,可以像其中添加NULL值,但是只能添加一次。比Treeset要快。

泛型
它可以表示好多个类型
泛型是JAVA SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类,接口和方法的创建中,分别称为泛型类,泛型接口,泛型方法。

ArrayList<Dog> a1=new ArrayList<Dog>(); 
        Dog  d1=new Dog();
        a1.add(d1);
        Dog dd=a1.get(0); //使用了泛型,此处不需要强转(Dog)

这里写图片描述

泛型的一个深入理解的案例:
代码如下

package com.stu;
public class Test1 {

    public static void main(String[] args) {

            Gen<String> g1=new Gen<String>("asaa");
            g1.showType();
    }

}
class Gen<T>{

    private T o;
    public Gen(T a){

        o=a;
    }
//得到T类型的名称
    public void showType(){
        System.out.println("类型是"+o.getClass().getName());
    }   
}

会输出:类型是java.lang.String
另一个差不多的案例:

package com.stu;

public class Test1 {

    public static void main(String[] args) {

            Gen<Bird> g1=new Gen<Bird>(new Bird());
            g1.showType();
    }

}
class Bird{
        public void say(){
                System.out.println("aa");
        }
        public void count(int a,int b){
            System.out.println(a+b);
        }

}
class Gen<T>{

    private T o;
    public Gen(T a){

        o=a;
    }
    public void showType(){
        System.out.println("类型是"+o.getClass().getName());
    }
}

则会输出:类型是com.stu.Bird
反射机制
类Gen 可以通过反射机制拿到类T的成员属性和成员方法的等类T所有的东西
例如下面代码:

Public void showTypeNaem(){
    Method [ ] m= o.getClass().getDeclaredMethods();
    For(int i=0;i<m.length;i++){
    System.out.println(m[i].getName); //我们可以通过此代码得出T类的构造函数
}

}

什么时候用泛型
当我们编辑一个类,但是不知道什么时候他是什么类,如果一会是INT一会是STRING我们就可以用泛型。

泛型的优点:
① 类型安全
② 向后兼容
③ 层次清晰
④ 性能较高,用GJ(泛型JAVA)编写的代码可以为JAVA编译器和虚拟机带来(通过反射机制)更多的类型信息,这些信息对JAVA程序做进一步优化提供条件。

异常处理
当出现程序无法控制的外部环境问题(用户提供的文件不存在,文件内容损坏,网络不可用。。。)时,JAVA就会用异常对象来描述。

JAVA中用2种方法处理异常
1. 在发生异常的地方直接处理
2. 将异常抛给调用者,让调用者处理

异常分类
① 检查性(编译)异常:java.lang.Excption
② 运行期异常:java.lang.RuntiomeException
③ 错误:java.lang.Error
顶层是java.lang.Throwable类,上面三个都是他的子类。

这里写图片描述

处理异常
(1)try….catch
程序运行产生异常时,将从异常发生点中断程序并向外抛出异常信息

Try{
    FileReader fr=new FileReader(“d:\\aa.text”);  //异常程序段
    //上面抛出异常,它下面的代码不会执行,会进入到catch语句
    //如果有多个catch语句,则进入匹配异常的那个语句
}catch(Exception e){  // 捕获异常
    e.printStackTrace();   // 打印异常
}

finally
如果把finally块置于try。。。catch。。。语句后,finally块一般都会得到执行,它相当于一个万能的保险,即使前面的try块发生异常,而又没有对应的异常的catch块,finally块将马上执行。
以下情形,finally块将不会被执行;
① finally块中发生了异常
② 程序所有在线程死亡
③ 在前面的代码中用了System . exit();
④ 关闭CPU

一般说把需要关闭的都写到finally里面去,例如,需要关闭的资源,文件,连接,内存等等。

 Finally{
    if(fr!=null){
        Try{
            Fr.close();  // 关闭 上面打开的FR,此时出现了异常也要处理一下
}catch(Exception e){
    e.printStackTrace():
}
}
}

Try ,catch,finally 不一定要同时出现。
但是只有finally 就不行了。

(2)异常抛出
抛出异常,让调用者去处理
一个案例

package com.stu;
import java.io.*;
public class Exc {


    public static void main(String[] args) {
        Father f=new Father();
        f.test2();
    }

}
class Father{
    private Son son;
    public Father(){
        son=new Son();
    }
    public void test2(){

        try {
            son.test();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class Son{

        public void test() throws Exception{  //此处抛出异常
     FileReader fr=null;
     fr=new FileReader("d:\\aa.txt");
     }
}

Swing
图形用户界面(Graphics User Interface gui)
这里写图片描述

一个简单的案例 ,显示界面
代码如下

package com.test;
import java.awt.*;
import javax.swing.*;
public class SwtSwing {


    public static void main(String[] args) {

        JFrame jf=new JFrame();
        jf.setTitle("HelloBoy");
        jf.setSize(200,200);
        jf.setVisible(true);

    }

}

显示效果如下
这里写图片描述

jf.setTitle("HelloBoy");//设置窗口的标题
        jf.setSize(200,200);//设置窗口的大小
        jf.setVisible(true);//设置显示窗口
        jf.setLocation(100, 200); //设置窗口的初始位置

    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口时 JVM也关闭了

ide (集成开发环境)
比如 jcreator ,vs2005 ,eclipse,都是IDE

布局管理器
组件在容器中的位置和大小是由布局管理器来决定的。所有的容器都会使用一个布局管理器,通过它来自动进行组件的布局管理。默认是边界布局。
种类
流式布局管理器(FlowLayout),边界布局管理器(BorderLayout),网格布局管理器(GridLayout),卡片布局管理器(GardLayout),网格包布局管理器(GridBagLayout)

边界布局管理器
简单的把容器划分中,东南西北中五个区域,中间区域最大
开发步骤
1. 继承JFrame
2. 定义你需要的组件
3. 创建组件(构造函数)
4. 添加组件
5. 对窗体设置
6. 显示窗体
注意事项:
五个区域并不是都要有,可以有的没有,默认是添加到中间
添加代码:
this.add(jb1,BorderLayout.CENTER);
this.add(jb2,BorderLayout.EAST);
this.add(jb3,BorderLayout.WEST);
this.add(jb4,BorderLayout.SOUTH);
this.add(jb5,BorderLayout.NORTH);
流式布局管理器
是按照组件添加的顺序将组件从左到右放置到容器中,当到达容器的边界时,组件将放置在下一行中。Flowlayout 可以以左对齐,居中对齐,以右对齐的方式排列组件。
添加代码:
this.setLayout(new FlowLayout(FlowLayout.LEFT));
只需要添加上面一句代码就可以了。
其他的不用变
禁止用户改变打开的窗体大小
This.setResizalbe(false);

注意事项:
1. 当容器被缩放时,组件的位置可能变化,但组件的大小不变
2. 不限制他所管理的组件大小,允许他们有最佳大小
3. 组件默认是居中对齐的

网格布局管理器
它将容器分割成多行多列,组件被填充到每个网格中,添加到容器中的组件首先放置在左上角的网格中,然后从左到右放置其它的组件,当占满该行的所有网格后,接着继续在下一行从左到右放置组件。
this.setLayout(new GridLayout(3,3,?,?));
只需要添加上面的代码就可以了
GridLayout 里面有4个参数,前两个是几行几列,后面的两个是与其它网格的左右间距和上下间距
注意事项:
1. 组件的相对位置不随容器的缩放而变化,但大小会变化。
2. 所有组件的大小相同

面板组件(Jpanel)
面板组件,非顶层容器,默认布局管理器是流式布局管理器
一个界面只可以有一个JFrame窗体组件,但可以有多个Jpanel面板组件,而JPanel上也可以使用FlowLayout,BorderLayout,GridLayout等各种布局管理器,这样就可以组合使用达到较为复杂的布局效果。

注意事项:
1. JPanel是JComponent的子类
2. 属于容器类组件,可以加入别的组件

其他组件
文本框 JTextField
密码框 JPasswordField
标签 JLabel
复选框 JCheckBox
单选框 JRadioButton 选项卡窗格(页签组件) JTabbedPane
同一组单选按钮必须先创建ButtonGroup,然后把单选框组件放入到ButtonGroup中。

下拉框 JComboBox
String jg[]={“北京”,”上海”,”天津”,”松原”};
jcb1=new JComboBox(jg);

列表框 JList
滚动窗格组件JScrollPane
String li[]={“故宫”,”泰山”,”西双版纳”,”武当山”,”天安门”};
list=new JList(li);
JScrollPane jsp=new JScrollPane(list);//定义滚动条 并把list加到里面
list.setVisibleRowCount(2); //设置滚动条的显示行数
jp2.add(jsp); //添加组件的时候直接添加jsp就可以了

拆分窗格 JSplitPane
属于容器类组件
下面是一个案例
代码如下:

package com.test;
import java.awt.*;
import javax.swing.*;
public class Others4 extends JFrame{

    JLabel jl1;
    JList jlist;
    JSplitPane jsp;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            new Others4();
    }
    public Others4(){

        String words[]={"what","is","am","are"};
        jlist=new JList(words);

        jl1=new JLabel(new ImageIcon("images/2.png"));

        jsp=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,jlist,jl1);
        jsp.setOneTouchExpandable(true); //设置可伸缩
        this.add(jsp);

        this.setSize(400,300);
        this.setTitle("拆分窗格案例");
        this.setLocation(200,200);
        this.setVisible(true);
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }
}

显示效果如下:
这里写图片描述

多行文本框组件 JTextArea

一个案例QQ聊天界面:
代码如下

package com.test;
import java.awt.*;
import javax.swing.*;
public class QQ extends JFrame{

    JPanel jp1;
    JTextArea jta;
    JComboBox jcb;
    JTextField jtf;
    JButton jb1;
    JScrollPane jsp;
    public static void main(String[] args) {
                new QQ();
    }
    public QQ(){
        jp1=new JPanel();
        String word[]={"拉登","奥巴马"};
        jcb=new JComboBox(word);
        jtf=new JTextField(10);
        jb1=new JButton("发送");
        jta=new JTextArea();
        jsp=new JScrollPane(jta);

        jp1.add(jcb);
        jp1.add(jtf);
        jp1.add(jb1);

        this.add(jsp);
        this.add(jp1,BorderLayout.SOUTH);

        this.setSize(400,200);
        this.setResizable(false);
        this.setLocation(200,200);
        this.setTitle("腾讯QQ"); 
        this.setIconImage((new ImageIcon("images/qq.png")).getImage());  //show QQ picture
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}

显示效果如下:
这里写图片描述

绘图技术
坐标体系
坐标原点左上角的,以像素为单位。像素是密度单位,厘米是长度单位
这里写图片描述

Component类提供了两个和绘图相关的方法
① paint(Graphics g)绘制组件的外观
② repaint() 刷新组件的外观
③ Graphics g 相当于一个画笔
一个案例
在面板上画圆
部分代码如下:

class Mypanel extends JPanel{

    public void paint(Graphics g){  

        super.paint(g);//这句话不能少 ,用来初始化父类函数

        g.drawOval(20, 20, 40, 40); 
    }
}

当组件第一次在屏幕显示的时候,程序会自动调用paint()方法来绘制组件。
在下面的情况下paint()将会被调用
① 窗口最小化,再最大化
② 窗口的大小发生变化
③ Repaint函数被调用

这里写图片描述
在面板上画图片
一个案例
代码如下:

class Mypanel1 extends JPanel{

    public void paint(Graphics g){

        super.paint(g);
        Image im=Toolkit.getDefaultToolkit().
getImage(Panel.class.getResource("/caocao.jpg"))、、、;
        g.drawImage(im,10,10,200,150,this);//代表这个PANEL
    }
}

画出字符串
g.setFont(new Font(“华文彩云”,Font.BOLD,14));
g.setColor(Color.red);
g.drawString(“祖国万岁”,100,100);

画坦克方法在程序里面

事件处理机制
一个案例
执行结果如下图
这里写图片描述

jb1.addActionListener(this);
        jb2.addActionListener(this);
        设置事件监听
        jb1.setActionCommand("黑色");
        jb2.setActionCommand("红色");
        设置监听指令
public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
        if(e.getActionCommand().equals("黑色")){
            jp.setBackground(Color.BLACK);
        }else if(e.getActionCommand().equals("红色")){
            jp.setBackground(Color.red);
        }
    }

监听事件处理方法

可以设置多个事件监听者,但是也需要先设置监听
任何一个类,只要它实现了相应的接口,他就可以去监听某个事件源。

一个类要实现监听的步骤:
① 实现相应的接口,keylistener,actionlister,mouselistener,windowlister
② 把接口的方法实现,根据需要重新编写
③ 在事件源上注册监听,设置监听
④ 事件传递是靠事件对象

事件源
事件源是一个产生或触发事件的对象,比如前面的JButton的一个对象jb1.当这个事件源对象的某个状态以某种方式发生变化时,就会产生某种类型的事件(一个事件源可能会发生多个不同类型的事件)。如果某个组件(对象)希望得到事件源产生的事件,就需要在这个事件源上设置监听。
事件
事件就是承载事件源状态改变时的信息对象。或者说,事件是事件源向事件监听器传输事件源状态信息的载体。在用户与GUI组件进行交互时就会生成事件,比如当鼠标在面板中移动时,就会生成一个鼠标移动事件的对象,而这个对象保存着当前鼠标在面板中位置信息。

常用的事件类型

这里写图片描述
事件监听器接口
事件监听者实际上就是一个类,该类实现了某个事件监听器接口。
事件监听器接口有多种,不同的事件监听器接口可以监听不同的事件,一个类可以实现一个事件监听接口,也可以实现多个监听接口。

KeyLister
keyPressed() 只要按下相应的按键就会触发此函数
keyTyped() 这个函数需要按下可能出现字符的按键,如a~z,但是F1就不会触发

注意事项:
这里写图片描述
线程
进程概念:进程是指运行中的应用程序,每个进程都有自己独立的地址内存空间(内存空间)。
线程概念:线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。线程有就绪,阻塞和运行三种基本状态。
线程的特点:
① 线程是轻量级的进程
② 线程没有独立的地址空间(内存空间)
③ 线程是由进程创建的(寄生在进程)
④ 一个进程可以拥有多个线程—这就是我们说的多线程编程
⑤ 线程有几种状态
A. 新建状态 new
B. 就绪状态 runnable
C. 运行状态 running
D. 阻塞状态 blocked
E. 死亡状态 dead

在JAVA中一个类要当做线程来使用有了两种方法
① 继承 Thread 类,并重写 run 函数
② 实现Runnable 接口,并重写 run函数

因为java是但继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能,java设计者们提供了另外一个方式创建线程,就是通过Runnable接口来创建线程。

这里写图片描述

不管是通过继承Thread,还是通过实现接口Runnable接口
一个线程对象只能启动线程一次;(a.start();),否则有异常抛出

Thread.currentThread().getName();得到当前线程的名字(一个编号)

线程并发带来了提高效率的好处,但是也带来了线程安全问题(如航空售票)
Java处理上面的问题方法如下

用synchronized(Object) { 你要同步的代码 } //同步代码块

Object对象锁

一个航空售票案例
代码如下
线程并发造成的安全问题

public class Locked {
    public static void main(String[] args) {
            Ticked t=new Ticked();

        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        Thread t3=new Thread(t);

        t1.start();
        t2.start();
        t3.start();
    }
}
class Ticked implements Runnable{
    private int nums=2000;
    public void run() {
        while(true)
            {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
synchronized (this) {
    if(nums>0){
System.out.println("第"+Thread.currentThread().getName()+"个售票员正在售出第"+nums+"张票");
                nums--;
                }
    if(nums<=0){
              break;
                }
            }
        }}}

这里写图片描述
IO编程

这里写图片描述

字节流(以字节方式读取byte):可以用于读写二进制文件及任何类型的文件
字符流:可以用于读写文本文件,不能操作二进制文件
只要记事本打不开的文件都是二进制文件

输入 字节流 InputStream 字符流 Reader
输出 字节流OutputStream 字符流 Writer

文件对象
文件数据源File类

一个案例
操作D盘的文件 aa.txt

package com;
import java.io.*;
public class TestIo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File f=new File("D:\\aa.txt");
        System.out.println("文件的路径"+f.getAbsolutePath());
        System.out.println("文件的大小"+f.length());
    }
}

创建文件:

File f1=new File("D:\\bb.txt");
        if(!f1.exists()){
            try {
                f1.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else {
            System.out.println("已有此文件");
        }
    }

创建文件夹

File f2=new File("D://ff");
        if(f2.isDirectory()){
            System.out.println("已有此文件夹");
        }else {
            f2.mkdir();
    }

写出一个文件夹下的文件

File f3=new File("D:\\JAVA");
        if(f3.isDirectory()){
            File list[]=f3.listFiles();
            for(int i=0;i<list.length;i++){
                System.out.println(list[i].getName());
            }
        }

读取存放在D盘下的aa.txt文件的内容

package com;
import java.io.*;
public class File2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File f=new File("D:\\aa.txt");
        FileInputStream fis=null;
        try {
             fis=new FileInputStream(f);
            byte b[]=new byte[1024];
            int n;
            while((n=fis.read(b))!=-1){
                String s=new String(b,0,n);
                System.out.println(s);
            }   
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally{
            try {
                fis.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

向文件中写入内容的一个案例

package com;
import java.io.*;
public class File3 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File f=new File("D:\\ss.txt");
        FileOutputStream fos=null;

        try {
            fos=new FileOutputStream(f);
            String s="我是中国人";
            fos.write(s.getBytes());

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally{
            try {
                fos.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

图片拷贝

package com;
import java.io.*;
public class PictureCopy {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File f=new File("d:\\图片.jpg");
        FileInputStream fis=null;
        FileOutputStream fos=null;
        try {
            fis=new FileInputStream(f);
            fos=new FileOutputStream("e:\\图片.jpg");
            int n;
            byte b[]=new byte[1024];
            while((n=fis.read(b))!=-1){
                fos.write(b);
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        finally{
            try {
                fis.close();
                fos.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }   
        }   
    }
}

字符流操作
操作的单位是char

package com;
import java.io.*;
public class CharFile {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        FileReader fr=null;
        FileWriter fw=null;

        try {
            fr=new FileReader("D:\\aa.txt");
            fw=new FileWriter("E:\\vv.txt");
            char c[]=new char[1024];
            int n;
            while((n=fr.read(c))!=-1){
                fw.write(c, 0, n);

            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        finally{
            try {
                fr.close();
                fw.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

文件字符流一次读取两个字节 效率比字节流(一次读取一个字节)高

缓冲字符流
操作的单位是String
按行读取
用BufferedReader 进行操作,先需要创建 FileReader
用Bufferedwriter 进行操作,先需要创建 Filewriter
一个案例 从一个地方读取文件复制到另一个地方
代码如下

package com;
import java.io.*;
public class FileBuffer {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        FileReader fr=null;
        FileWriter fw=null;
        BufferedReader br=null;
        BufferedWriter bf=null;
        try {
            fr=new FileReader("D:\\aa.txt");
            br=new BufferedReader(fr);
            fw=new FileWriter("E:\\ee.txt");
            bf=new BufferedWriter(fw);
            String s="";
            while((s=br.readLine())!=null){
                fw.write(s+"\r\n");
            }

        } catch (Exception e) {
            // TODO: handle exception
        }
        finally{
            try {
                br.close();
                bf.close();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }

        }
    }

}

记事本开发
JFileChooser 文件选择组件
用JAVA操作声音文件

package com;
import java.io.*;
import javax.sound.*;
import javax.sound.sampled.*;
public class Audio {
    public static void main(String[] args) {
        AePlayWave a=new AePlayWave("D:\\111.wav");
        a.start();
    }
}
//播放声音的类
class AePlayWave extends Thread {

    private String filename;
    public AePlayWave(String wavfile) {
        filename = wavfile;

    }
    public void run() {

        File soundFile = new File(filename);

        AudioInputStream audioInputStream = null;
        try {
            audioInputStream = AudioSystem.getAudioInputStream(soundFile);
        } catch (Exception e1) {
            e1.printStackTrace();
            return;
        }

        AudioFormat format = audioInputStream.getFormat();
        SourceDataLine auline = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

        try {
            auline = (SourceDataLine) AudioSystem.getLine(info);
            auline.open(format);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        auline.start();
        int nBytesRead = 0;
        //这是缓冲
        byte[] abData = new byte[512];

        try {
            while (nBytesRead != -1) {
                nBytesRead = audioInputStream.read(abData, 0, abData.length);
                if (nBytesRead >= 0)
                    auline.write(abData, 0, nBytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        } finally {
            auline.drain();
            auline.close();
        }

    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值