Java知识点总结


[TOC]

1.语法

class_2.pptx

1.1变量

1.2常量

  • 不可改变

  • 用关键字 final 定义

    final double PI=3.14159265;
  • 只能赋值一次

1.3表达式

  • 操作符+操作数

  • 会被计算成一个确定类型的数

1.4赋值

1.5基本数据类型和引用类型

  • 基本数据类型: int、double …

  • 引用数据类型:对象、数组

    image-20210604232429013

1.5.1默认值

Data Type Default Value
boolean false
byte 0
char ‘\u000’
short 0
int 0
long 0L
float 0.0f
double 0.0d
Any class type null

1.5.2 boolean

boolean truth=true;

boolean有两种: true和false

1.5.3 char

1.5.4 String

它不是一个基本数据类型,而是一个类。

1.5.5 byte,short,int,long

  • Decimal 10,octal 8,hexadecimal 16

  • 0开头的是8进制

    byte b1=077;
  • 0x开头的是16进制

    byte b2=0xBAAC;
  • L/l 结尾的是long

    byte b3=0x03L;

1.5.5.1范围

Integer Length Name or Type Range
8 bits byte -2^7 - 2^7-1
16 bits short -2^15 - 2^31-1
32 bits int -2^31 - 2^31-1
64 bits long -2^63 - 2^63-1

1.5.6 float和double

  • E/e 指数
  • F/f float
  • D/d double

32 bits

64 bits

1.5.6 操作符

算术操作符只适用于基本数据类型( byte, short, int, long, float, double, char),不适用与boolean

  • int/int=>int

  • 如果操作数是byte,short或者char,那么结果会转换为int

    image-20210604235242125

1.5.7 隐式类型转换

当两个操作数的类型不同时,小类型会自动转换为大类型

image-20210605000020622

总结:

image-20210605000309901

1.6 控制语句

1.6.1顺序

1.6.2分支

  • if,else

  • switch

    switch(表达式)
    {
      case 常量1:...    break;
      case 常量2:...    break;
      ...
      default:
         ...
         break;
    }

1.6.3循环

  • for
  • enhanced for loop(for each)
  • while

2.数组和字符串

class_3_array_string.pptx

2.1 print I/0

  • 标准输入设备

    System.in
  • 标准输出设备

    System.out
  • 标准错误设备

    System.err

2.1.1 Scanner

  • 所在包:java.util
import java.util.Scanner;public class test1{    public static void main(String[] args){        int num1;        double num2;        String str;        Scanner in=new Scanner(System.in);        System.out.println("Enter an integer:");        num1=in.nextInt();        System.out.println("Enter a floating point:");        num2=in.nextDouble();        System.out.printf("%s, Sum of %d & %.2f is %.2f \n",str,num1,num2,num1+num2);        in.close();    }}

2.1.1.1 nextLine

import java.util.Scanner;public class test1{    public static void main(String[] args){        int num1;        double num2;        String str;        Scanner in=new Scanner(System.in);        System.out.println("Enter a string(with space):");        str=in.nextLine();        System.out.printf("%s%n",str);        in.close();    }}

2.1.1.2 input source

2.2 char

  • 在算术操作中,char会被首先转换成int类型。

  • 在java中,运算符只能运用于 int,long,float或者double类型,不能应用与byte,short,char类型。

    char c1='0';char c2='A';char c3;//c3=c1+c2   err:RHS evaluated to "int",cannot assign to LHS of "char"c3=(char)(c1+c2);System.out.println(c3); //print 'q' System.out.println(c1+c2); //print int 113System.out.println((char)(c1+c2));  //print cahr 'q'
  • 复合运算符(像 +=,-=,*=,/=,%=)它们作用于int类型,但是结果会自动转换成等式的左值类型

  • 对于char(和byte,和short)的increment(++)和decrement(–),不转换为int。

2.2.1 char转换为int

  • 可以通过 -‘0’ 的方式将char类型转换为int类型。

    char hexChar='a';int dec;if(hexChar>='0'&&hexChar<='9'){dec=hexChar-'0';  //int 0-9}else if(hexChar>='A'&&hexChar<='F'){dec=hexChar-'A'+10;//int 10-15}else if(hexChar>='a'&&hexChar<='f'){dex=hexChar-'a'+10;  nt 10-15}else{dex=-1;System.out.println("invalid hex char");}System.out.println(hexChar+":"+dec);

2.3 String

不是基本数据类型,而是一个类

String greeting="Good Morning!\n";String errorMessage="Record Not Found!";
  • str.length():返回字符串的长度
  • str.charAt(int index):返回字符串index位置的字符
  • str1.equals(str2):比较两个字符串是否相等,注意:不能用“str1==str2”来比较

2.3.1将字符串转为基本数据类型

  • 将字符串转为整型
Integer.parseInt(anlntStr)
  • 将字符串转为浮点型
Double.parseDouble(aDoublestr)

或者

Float.parseFloar(aFloatStr)

2.3.2将基本数据类型转为字符串

方法:

  • 用“+”连接基本数据类型和空的字符串“”

  • 使用 String.valueof(aPrimitive)

  • 使用toString()方法

    Integer.toString(anInt)Double.toString(aDouble)Character.toString(aChae)Boolean.toString(aBoolean)
  • 例:

    String str1=123+"";  //int 123->String"123"String str2=12.34+"";//double 12.34->String"12.34"String str3='c'+"";//char'c'->String "c"String str4=true+"";//boolean true->String"true"//Using String.valueOf(aPrimitive)(applicable to ALL primitive types)String str5=String.valueOf(12345);//int 12345->String"12345"String str6=String.valueOf(true);//boolean true->String"true"String str7=String.valueOf(55.66)//double 55.66->String"55.66"//Using toString() for each primitive typeString str8=Integer.toString(1234);//int 1234->String "1234"String str9=Double.toString(1.23);//double 1.23->String "1.23"String str10=Character.toString("z");//char'z'->String "z"

2.3.3 Formatting Strings

  • String.format() 返回字符串的格式
  • String.format() print()时有相同的格式
  • 字符串逆序输出:
import java.util.Scanner;public class test1{    public static void main(String[] args){        String inStr;        int inStrlen;        Scanner in=new Scanner(System.in);        System.out.print("Enter a String");        inStr=in.next();        inStrlen=inStr.length();        System.out.println("The reverse is:");        for(int inCharIdx=inStrlen-1;inCharIdx>=0;--inCharIdx)        {            System.out.print(inStr.charAt(inCharIdx));        }        in.close();    }}
  • 二进制转十进制:
package test;import javafx.scene.chart.LineChart;import java.util.Scanner;public class test1{    public static void main(String[] args){        String binStr;        int binStrlen;        int dec=0;        char binChar;        Scanner in=new Scanner(System.in);        System.out.print("Enter a binary string :");        binStr=in.next();        binStrlen=binStr.length();        for(int exp=0;exp<binStrlen;++exp){            binChar=binStr.charAt(binStrlen-1-exp);            if (binChar=='1'){                dec+=(int)Math.pow(2,exp);            }else if(binChar=='0'){                            }            else {                System.out.println(("error:invalid bonary string\""+binStr+"\""));                return;            }        }        System.out.println("The equivalent decimal for\""+binStr+"\"is "+dec);        in.close();    }}

2.4 数组

建数组前要知道它的长度并根据它的长度分配空间。

一旦数组被建立了,在运行时就不能在改变它的长度了。

  • Enhanced for-loop

    int[] numbers={8,2,6,4,3};int sum=0;sumSq=0;for(int number:numbers){    sum+=number;    sumSq+=number*number;}System.out.println("The sum is:"+sum);System.out.println("The square sum is:"+sumSq);

2.4.1 创建数组

char[] s;s=new char[26];for(int i=0;i<26;i++){s[i]=(char)('A'+i);}

2.4.2 创建类的数组

Point[] p;p=new Point[10];for(int i=0;i<10;i++){p[i]=new Point(i,i+1);}

2.4.3 初始化数组

String[] names;names=new String[3];names[0]="Georgianna";names[1]="Jen";names[2]="Simon";String[] names={"Georfianna","Jen","Simon"};MyDate[]dates;dates=new MyDate[3];dates[0]=new MyDate(22,7,1964);dates[1]=new MyDate(1,1,2000);dates[3]=new MyDate(22,12,1964);MyDate[] dates={new MyDate(22,,1964),new MyDate(1,1,2000),new MyDate(22,12,1964)}

2.4.4 读数组

System.out.print("The values are:[");for(int i=0;i<item.length;i++){	if(i==0){	System.out.print(items[0]);	}else{	System.out.print(", "+items[i]);	}}

2.4.5 数组转字符串

import java.util.Arrays;
int[] a1={6,1,3,4,5};int[] a2={};double[] a3=new double[1];System.out.println(Arrays.toString(a1));System.out.println(Arrays.toString(a2));System.out.println(Arrays.toSting(a3));

2.4.6 多维数组

//1int[][]=twoDim=new int[4][];twoDim[0]=new int[5];twoDim[1]=new int[5];//errint[][] twoDim=new int[][4]; //illegal//2int[][] twoDim=new int[4][5];
  • exercise:
package test;import javafx.scene.chart.LineChart;import java.util.Scanner;public class test1{    public static void main(String[] args){       int[][] grid={               {1,2},               {3,4,5},               {6,7,8,9}       };       for(int y=0;y<grid.length;++y){           for(int x=0;x<grid[y].length;++x){               System.out.printf("%2d",grid[y][x]);           }           System.out.println();       }       int [][] grid1=new int[3][];       grid1[0]=new int[2];       grid1[1]=new int[3];       grid1[2]=new int[4];       for(int y=0;y< grid1.length;++y){           for(int x=0;x<grid[y].length;++x){               System.out.printf("2d",grid[y][x]);           }            System.out.println();       }    }}
  • 运行结果:

1 2
3 4 5
6 7 8 9
2d2d
2d2d2d
2d2d2d2d

3.方法

class_4_method.pptx

3.1过程

  1. caller 寻找方法,然后把参数传递给方法

  2. 方法:

    a)接受从caller传递的参数

    b)执行方法并将结果传回caller

  3. caller接受结果,并且继续它的操作

3.2例

public class test1{    public static void main(String[] args) {        double r = 1.1, area, area2;        area = getArea(r);        System.out.println("area 1 is: " + area);        area2 = getArea(2.2);        System.out.println("area 2 is: "+area2);        System.out.println("area 3 is: "+getArea(3.3));    }    public static double getArea(double radius){        return radius*radius*Math.PI;    }}
  • output

    area 1 is: 3.8013271108436504
    area 2 is: 15.205308443374602
    area 3 is: 34.21194399759284

  1. Magic Number

3.3方法的语法

3.3.1 形参与实参

  1. 原语类型参数的传递值
  2. 通过数组或者对象传递引用类型
public class test1 {    public static void main(String[] args) {        int[] testArray = {9, 5, 6, 1, 4};        System.out.println("In caller, before calling the method, aray is:" + Arrays.toString(testArray));        increment(testArray);        System.out.println("Inside method, after calling the method, array is "+Arrays.toString(testArray));        }    public static void increment(int[] array) {        System.out.println("Inside method, before operation, array is " + Arrays.toString(array));        for(int i=0;i<array.length;i++)++array[i];        System.out.println("Inside method, after operation, array is "+Arrays.toString(array));    }}
  • output

In caller, before calling the method, aray is:[9, 5, 6, 1, 4]
Inside method, before operation, array is [9, 5, 6, 1, 4]
Inside method, after operation, array is [10, 6, 7, 2, 5]
Inside method, after calling the method, array is [10, 6, 7, 2, 5]

3.3.1.1

  • JDK5引入了变量参数(或varargs)和新语法“Type…”。
  • Varargs只能用于最后一个参数。
  • 三个点(…)表示最后一个参数可以作为数组或逗号分隔的参数序列传递。
  • 编译器会自动将vararg打包到一个数组中。然后,可以在方法体中检索和处理这些参数中的每一个作为数组。
3.3.1.1.1例
public class test1 { public static  void doSomething(String...strs){    System.out.print("Arguments are: ");     for (String s:strs) {         System.out.print(s+", ");     }     System.out.println();    }    public static  void doSomething(String s1,String s2){    System.out.println("Overloaded version with 2 args: "+s1+", "+s2);    }    public static void main(String...args){        doSomething("Hello","world","again","and","again");        doSomething("Hello","world");        String[] strs={"apple","orange"};        doSomething(strs);    }}
  • Output

Arguments are: Hello, world, again, and, again,
Overloaded version with 2 args: Hello, world
Arguments are: apple, orange,

3.4 方法的重载

在Java中,一个方法(一个特定的方法名)可以有多个版本,每个版本对不同的参数集进行操作—称为方法重载。

版本应根据参数的编号、类型或顺序进行区分。

3.5方法的类型转换

参数列表里含double的方法可以接受任何数值基元类型,如int或float。这是因为执行了隐式类型转换。

public class test1 {    public static void main(String[] args){       System.out.println(average(8,6));  //1       System.out.println(average(8,6,9));//2       System.out.println(average(8.1,6.1));//3       System.out.println(average(8,6.1));//3    }    public static int average(int n1,int n2){        System.out.println("------v1------");        return (n1+n2/2);    }    public static int average(int n1,int n2,int n3){        System.out.println("------v2------");        return (n1+n2+n3)/3;    }    public static double average(double n1,double n2){        System.out.println("------v3------");        return (n1+n2)/2.0;    }}
  • Output

——v1——
11
——v2——
7
——v3——
7.1
——v3——
7.05

3.6main

3.6.1命令行参数

public class test1 {    public static void main(String[] args){       int operand1,operand2;       char theOperator;       operand1=Integer.parseInt(args[0]);       operand2=Integer.parseInt(args[1]);       theOperator=args[2].charAt(0);       System.out.print(args[0]+args[2]+args[1]+"=");       switch (theOperator){           case('+'):System.out.println(operand1+operand2);break;           case('-'):System.out.println((operand1-operand2));break;           case('*'):System.out.println(operand1*operand2);break;           case('/'):System.out.println(operand1/operand2);break;           default:System.out.print("inError:invalid operator!");       }    }}

3.7 内存

3.7.1 堆

  • 堆空间被java运行时用来为对象和JRE类分配内存。

  • 每当我们创建一个对象时,它总是在堆空间中创建的。

  • 垃圾回收在堆内存上运行,以释放没有任何引用的对象所使用的内存。

3.7.2 栈

  • 堆栈内存用于执行线程。

  • 它们包含特定于方法的短期值,以及对堆中从该方法引用的其他对象的引用。

  • 堆栈内存总是按后进先出的顺序引用。

  • 每当调用一个方法时,都会在堆栈内存中为该方法创建一个新块,以保存本地原语值和对该方法中其他对象的引用。

  • 一旦该方法结束,该块就变得不可用,并可用于下一个方法。

3.7.3 Overview

1.堆内存由应用程序的所有部分使用,而堆栈内存仅由一个执行线程使用。

2.每当创建对象时,它总是存储在堆空间中,堆栈内存包含对它的引用。堆栈内存只包含本地原语变量和堆空间中对象的引用变量。

3.堆中存储的对象是全局可访问的,而堆栈内存不能被其他线程访问。

4.当堆栈内存满时,Java运行时抛出Java.lang.StackOverflowerError,而如果堆内存满,则抛出Java.lang.OutOfMemoryError:Java堆空间错误。

3.7.4 堆栈跟踪

image-20210606135250614

  • 堆栈跟踪是一个异常列表(或“原因”),从最表面的异常(如服务层异常)到最深层的异常(如数据库异常)。

  • 就像我们称之为“stack”的原因是因为stack是先进先出(FILO),最深的异常在一开始就发生了,然后一系列的异常产生了一系列的后果,表面异常是最后一个及时发生的异常,但是我们在第一时间看到了它。

关键1:这里需要理解的一件棘手而重要的事情是:最深层的原因可能不是“根本原因”,因为如果你写了一些“坏代码”,它可能会导致一些比它的层更深的异常。

关键2:另一个棘手但重要的事情是在每个“原因”块内,第一行是最深的一层,发生在这个块的第一位。

3.8 debug

  • Compilation Error (or Syntax Error):
  • Runtime Error:
  • Logical Error:

4.面向对象

class_5_oop_1.pptx

class_6_oop_2.pptx

class_7_oop_3.pptx

4.1OOP

4.1.1 class

  • 组成:

​ 名称

​ 变量

​ 方法

4.1.2 语法

[AccessControlModifier] class ClassName{    //class body contains members(variables and methods)    ......}

image-20210607130443432

4.1.2.1 成员变量与方法

4.1.2.1.1 静态成员

keyword:static

4.1.2.2 构造函数

  • 构造函数是一种特殊的方法,它的方法名与类名相同

  • 构造函数方法不同于普通方法:

  • 这个 名称 构造函数方法的名称必须与类名相同。

  • 构造函数的方法标题中没有返回类型。它隐式返回void。

  • 构造函数只能通过“new”操作符调用。

  • 它只能用于初始化构造的实例一次。一旦构造了实例,就不能再调用构造函数了。

  • 构造函数是不继承的。每个类都应该定义自己的构造函数。

  • 默认构造函数: 没有参数的构造函数称为 默认构造函数。它将成员变量初始化为其默认值。

class Circle{    private double radius;    private String color;    public Circle() {        radius = 1.0;        color = "red";    }    public Circle(double r){        radius =r;        color="red";    }    public Circle(double r, String c){        radius=r;        color=c;    }    public double getRadius(){        return radius;    }    public String getColor(){        return color;    }    public double getArea(){        return radius*radius*Math.PI;    }}public class test1 {    public static void main(String[] args){       Circle c1=new Circle(2.0,"blue");       System.out.println(c1.getRadius());       System.out.println(c1.getColor());       System.out.println(c1.getArea());       Circle c2=new Circle(2.0);       System.out.println(c2.getRadius());       System.out.println(c2.getColor());       System.out.println(c2.getArea());       Circle c3=new Circle();       System.out.println(c3.getRadius());       System.out.println(c3.getColor());       System.out.println(c3.getArea());    }}
  • Output

2.0
blue
12.566370614359172
2.0
red
12.566370614359172
1.0
red
3.141592653589793

Process finished with exit code 0

4.1.2.3 方法重载

  • 方法重载意味着同一方法名可以有不同的实现(版本)。

  • 不同的实现必须通过其参数列表(参数的数量、类型或顺序)来区分。

  • 重载类的构造函数 :构造函数和普通方法一样,也可以重载。

  • 根据调用方法时使用的实际参数列表,将调用匹配的构造函数。

  • 如果参数列表与其中任何一个方法都不匹配,则会出现编译错误。

4.1.3 access

访问控制修饰符可用于控制类、成员变量或类内成员方法的可见性。

public:系统中的所有其他对象都可以访问和使用类/变量/方法。

private:类/变量/方法只能在此类中访问和使用。

4.1.5 “this”

4.1.6 toString

可以通过调用instanceName.toString()显式调用toString()方法,也可以通过println()或字符串连接运算符“+”隐式调用。也就是说,运行println(实例)会隐式调用该实例的toString()方法。

public String toString(){    return "Circle[raius="+radius+",color="+color+"]";}Circle c4= new Circle();System.out.println(c4.toString())//显示调用  c4.toString()    System.out.println(c4);//隐式调用c4.toString()System.out.println("c4 is:"+c4);

4.1.7 常量

final修饰常量

常量是用final修饰符定义的变量。最后一个变量只能赋值一次,赋值后不能修改其值。

期末考试的高级笔记:

1.常量基本变量不能重新赋值。

2.常量实例不能重新分配新对象。

3.常量类不能被继承(或被扩展)。

4.不能重写final方法。

4.2 Composition

4.3 继承

image-20210608154248413

继承结构是树状的。

4.3.1 “extends”关键字

class GoalKeeper extends SccoerPlayer{......}class MyApplet extends java.applet.Applet{......}class Cylinder extends Circle{......}

image-20210608154613133

package test;import com.sun.org.apache.bcel.internal.generic.NEW;import com.sun.org.apache.xpath.internal.objects.XString;import javafx.scene.chart.LineChart;import kotlin.jvm.internal.FunctionReferenceImpl;import kotlin.text.UStringsKt;import javax.print.attribute.standard.RequestingUserName;import java.util.*;import java.lang.reflect.Array;import java.util.Arrays;import java.util.Arrays;import java.util.List;import java.util.ArrayList;public class test1 {        private double radius;        private String color;        public test1(){            this.radius=1.0;            this.color="red";            System.out.println("Constructed a test1 with test1()");        }        public test1(double raiuds){            this.radius=radius;            this.color="red";            System.out.println("Constructed a test1 with test1(radius)");        }        public test1(double radius,String color){            this.radius=radius;            this.color=color;            System.out.println("Constructed a test1 with test1(radius,color)");        }        public double getRadius(){            return this.radius;        }        public String getColor(){            return this.color;        }        public void setRadius(double radius){            this.radius=radius;        }        public void setColor(String color){            this.color=color;        }        public String toString(){            return radius+" "+color;        }        public double getArea() {            return radius * radius * Math.PI;        }    public static void main(String[] args) {        Cylinder cy1 = new Cylinder();        System.out.println("Radius is"+cy1.getRadius()+",Height is "+cy1.getHeight()+",Color is "+cy1.getColor()+",Base area is "+cy1.getArea()+",Volume is "+cy1.getVolume());        Cylinder cy2=new Cylinder(5.0,2.0);        System.out.println("Radius is "+cy2.getRadius()+",Height is "+cy2.getHeight()+",Color is "+cy2.getArea()+", Volume is "+cy2.getVolume());    }}package test;import javax.sound.midi.SysexMessage;public class Cylinder extends test1{    private double height;    public Cylinder() {        super();        this.height=1.0;        System.out.println("Constructed a Cylinder with Cylinder()");    }    public Cylinder(double height){        super();        this.height=height;        System.out.println("Constructed a Cylinder(height)");    }    public Cylinder(double height,double radius){        super(radius);        this.height=height;        System.out.println("Constructed a Cylinder with Cylinder(height,radius)");    }    public Cylinder(double height,double radius,String color){        super(radius,color);        this.height=height;        System.out.println("Constructed a Cylinder with Cylinder(height,radius,color)");    }    public double getHeight(){        return this.height;    }    public void setHeight(double height){        this.height=height;    }    public double getVolume(){        return getArea()*height;    }    public String toString(){        return "this is a Cylinder";    }}
  • Output

Constructed a test1 with test1()
Constructed a Cylinder with Cylinder()
Radius is1.0,Height is 1.0,Color is red,Base area is 3.141592653589793,Volume is 3.141592653589793
Constructed a test1 with test1(radius)
Constructed a Cylinder with Cylinder(height,radius)
Radius is 0.0,Height is 5.0,Color is 0.0, Volume is 0.0

4.4 方法重写+变量隐藏

子类从其超类(直接父类及其所有祖先)继承所有成员变量和方法。

它可以使用继承的方法和变量。

它还可以通过提供自己的版本来覆盖继承的方法,或者通过定义相同名称的变量来隐藏继承的变量。

public class Cylinder extends Circle{	@Override	public double getArea(){        return 2*Math.PI*get.Radius()*height+2*super.getArea();    }    public double getVolume(){        return super.getArea()*height;    }    @Override    public String toString(){        return "Cylinder["+super.toString+",height"+height+']';    }}

方法重载意味着相同的方法名称可以有不同的实现(版本)。

不同的实现必须通过其参数列表(参数的数量、参数类型或顺序)来区分。

超载 类“构造函数”

构造函数,就像一个普通方法一样,也可以重载。

根据调用方法时使用的实际参数列表,将调用匹配构造函数。

如果参数列表与任何方法不匹配,则会出现编译错误。

4.4.1 重写变量

“@Override”被称为annotation(在jdk1.5中引入),它要求编译器检查超类中是否存在要重写的方法。

如果将要重写的方法的名称拼写错误,这将非常有帮助。

@重写注释是可选的,但是拥有它确实很好。

4.4.2 “super”关键字

关键字this引用此实例。

关键字super指的是超类,它可以是直接父类或其祖先。

关键字super允许子类访问子类定义中的超类方法和变量。

超级变量名

超级方法()

super()和super(argumentList)可以用来调用超类的构造函数。

在构造函数的主体中,可以使用super(args)调用其直接超类的构造函数。

请注意,如果使用super(args),则它必须是子类构造函数中的第一个语句。

如果构造函数中没有使用它,Java编译器会自动插入一个super()语句来调用其直接超类的no arg构造函数。

4.4.3 单继承

  • java不支持多重继承(C++)。

  • 多重继承允许子类具有多个直接超类。

​ 如果超类对于同一方法有冲突的实现,那么这有一个严重的缺点。

  • 在Java中,每个子类只能有一个直接超类,即单继承。另一方面,一个超类可以有许多子类。

4.4.4 共同根类

Java采用了所谓的公共根方法。

所有Java类都派生自一个名为Java.lang.Object的公共根类。

这个对象类定义并实现在JRE下运行的所有Java对象所需的公共行为。这些常见行为支持多线程和垃圾收集器等功能的实现。

image-20210608173134409

回想一下,重用现有类有两种方法:组合和继承。

组合呈现出一种“has-a”关系。

子类超类表现出所谓的“is-a”关系。

经验法则:在考虑继承之前,尽可能使用组合。仅当类之间存在明确的层次关系时才使用继承。

4.3 多态

“多态性”一词的意思是“多种形式”。

它来自希腊语“poly”(意思是多)和“morphos”(意思是形式)。

可替代性

子类拥有其超类的所有属性和操作。

这意味着子类对象可以做它的超类可以做的任何事情。

因此,当需要一个超类实例时,我们可以替换一个子类实例,一切都会正常工作。

4.3.1 Upcasting&Downcasting

Java中有两种类型转换:

1.如上所述,通过类型转换运算符进行显式类型转换

2.如果没有精度损失,编译器自动执行隐式类型转换

在Java中,如果尝试将的double、float或long值赋给int变量,则会得到编译“error:incompatible types:possible lossy conversion from double | float | long to int”。这是因为小数部分将被截断并丢失。

4.3.1.1 隐式类型转换与类型转换运算符

要将双精度值赋给int变量,需要调用所谓的类型转换运算符(以(int)doubleOperand的形式)对双精度操作数进行操作,并返回int中的截断值。

//显式类型转换double d =3.5;int i;i=(int)d;//隐式类型转换int i=3;double d;d=i;d=(double)i;double aDouble =55;double nought=0;

下图显示了编译器执行隐式类型转换的顺序。

规则是将较小的类型提升为较大的类型,以防止精度损失,称为加宽转换。

image-20210608174703378

4.4.1.1.1 upcasting

将子类实例向上转换为超类引用

用子类实例替换其超类称为“向上投射”。

向上转换总是安全的,因为子类实例拥有

Circle c1 = new Cylinder(1.1,2.2);Circle c2 = new String();
4.4.1.1.2 downcasting

向下转换对其原始类的替换引用

可以将替换的实例还原回子类引用。这被称为“向下投射”。

向下转换需要前缀运算符(new-type)形式的显式类型转换运算符。向下转换并不总是安全的,如果要向下转换的实例不属于正确的子类,则会引发运行时ClassCastException。

Circle c1= new Cylinder(1.1,2.3);  //upcast is safeCylinder cy1=(Cylinder)c1;  //downcast needs casting operator

4.3.2 “instanceof”运算符

Java提供了一个名为instanceof的二进制操作符,如果对象是特定类的实例,则返回true。

Circle c1=new Circle(); System.out.println(c1 instanceof Circle); //trueif(c1 instanceof Circle);{......}

4.3.3 总结

多态性综述

子类实例处理其超类的所有属性操作。当需要一个超类实例时,它可以被一个子类实例替换。换句话说,对一个类的引用可能包含该类的一个实例或者它的一个子类的一个实例,这被称为可替换性。

如果将子类实例分配给超类引用,则只能调用在超类中定义的方法。不能调用子类中定义的方法。

但是,被替换的实例在重写方法和隐藏变量方面保留了自己的标识。如果子类重写了超类中的方法,则将执行子类的版本,而不是超类的版本。

动态绑定或后期绑定

我们通常不把对象当作它自己的类型,而是当作它的基类型(超类或接口)。这允许您编写不依赖于特定实现类型的代码。

然而,这带来了一个新问题。编译器在编译时无法精确地知道在运行时将执行哪段代码(例如,getArea()对于矩形和三角形有不同的实现)。

为了支持多态性,面向对象语言使用了一种不同的机制,称为动态绑定(或后期绑定或运行时绑定)。调用方法时,只在运行时确定要执行的代码。在编译过程中,编译器检查方法是否存在,并对参数和返回类型执行类型检查,但不知道在运行时执行哪段代码。

虽然动态绑定解决了支持多态性的问题,但它也带来了另一个新问题。编译器无法检查类型转换运算符是否安全。它只能在运行时进行检查(如果类型检查失败,则抛出ClassCastException)。

JDK1.5引入了一个名为泛型的新特性来解决这个问题。

4.4 抽象类&接口

4.4.1 抽象类

抽象方法和抽象类。

抽象方法是只有签名(即方法名、参数列表和返回类型)而没有实现(即方法体)的方法。您可以使用关键字abstract来声明抽象方法。

这些抽象方法的实现将在知道实际形状之后提供。无法调用这些抽象方法,因为它们没有实现。

包含一个或多个抽象方法的类称为抽象类。抽象类必须用类修饰符抽象声明。抽象类不能实例化,因为它的定义不完整。

abstract public class Shape{    ......    ......   abstract public double getArea();   abstract public double getPerimeter();   abstract public void draw();}abstract public class Shape{    private String color;    public Shape(String color){        this.color=color;    }    @Override    public String toString(){        return "Shape[color"+color+"]";    }    abstract public double getArea();}public class TestShape{    public static void main(String[] args){        Shape s1= new Rectangle("red",4,5);        System.out.println(s1);        System.out.println("Area is"+s1.getArea());                Shape s2=new Triangle("blue",4,5);        Shape s3=new Shape("green");    }}

要使用抽象类,必须从抽象类派生子类。在派生的子类中,必须重写抽象方法并为所有抽象方法提供实现。

派生的子类现在已经完成,并且可以实例化(如果子类没有为超类的所有抽象方法提供实现,那么子类仍然是抽象的。)

再加上多态性,您可以将子类实例向上转换为Shape,并在Shape级别编程,即在接口上编程。

接口和实现的分离使得软件设计更好,易于扩展。

经验法则:

抽象方法不能声明为final,因为final方法不能被重写。另一方面,抽象方法必须在子类中重写才能使用。

抽象方法不能是私有的(这会生成编译错误)。这是因为私有方法对子类不可见,因此不能被重写。

4.4.2 接口

Java接口是一个100%抽象的超类,它定义了它的子类必须支持的一组方法。

接口只包含公共抽象方法(带有签名但没有实现的方法)和可能的常量(公共静态最终变量)。

必须使用关键字“interface”来定义接口(而不是普通类的关键字“class”)。它的抽象方法不需要关键字public和abstract,因为它们是必需的。

与抽象超类类似,接口不能实例化。

您必须创建一个实现接口的“子类”,并提供所有抽象方法的实际实现。

4.4.2.1 “implements”关键字

  • 关键字“implements”派生子类。

    在Java中,抽象类和接口用于将类的公共接口与其实现分离开来,从而允许程序员在接口上而不是在各种实现上进行编程。

public interface interfacename extends  superinterfacename{     		static final...;}
public interface Shape{    double getArea();}public class Rectangle implements Shape{    private int length,width;    public Rectangle(int length,int width){        this.length=lenth;        this.width=width;    }    @Override    public String toString(){        return "Rectangle[length="+length+", width="+width+"]";    }    public double getArea(){        return length*width;    }}public class Triangle implements Shape{     private int base,height;    public Triangle(int base,int height){        this.base=base;        this.height=height;    }    @Override    public String toString(){        return "Traingle[base="+base+", height="+height+"]";    }    @Override    public double getArea(){        return 0.5*base*height;    }}public class TestShape{    public static void main(String[] args){        Shape s1=new Rectangle(1,2);        System.out.println(s1);        System.out.println("Area is "+ s1.getArea());        Shape s2=new Traingle(3,4);        System.out.println(s2);                System.out.println("Area is "+s2.getArea());        Shape s3=new Shape("green");    }}

Java只支持单个继承。也就是说,子类可以从一个和只有一个超类派生。Java不支持多继承,以避免从多个超类继承冲突的属性。

然而,子类可以实现多个接口。Java中允许这样做,因为接口只定义抽象方法而不需要实际实现,并且不太可能从多个接口继承冲突的属性。

换句话说,Java通过实现多个接口间接地支持多个继承。

4.5 静态变量/方法

  1. 静态变量/方法属于类,它被所有实例共有,因此,它也被称为 类变量/方法。

  2. 非静态成员/方法属于一个类的实例,也被称为实例变量。

  3. 每一个实例都有它的存储空间。

  4. 每一个实例变量/方法都有它们自己在实例的副本,他们和其他实例共享。

  5. 静态变量/方法在类中有一个公共内存位置,由所有实例共享。

  6. JVM在类加载期间分配静态变量。即使没有创建实例,静态变量也存在。

    AClassName.aStaticVariableName或AClassName.aStaticMethodName()。

  7. 非静态变量/方法属于实例。要使用非静态变量/方法,必须首先构造实例。

  8. 静态变量/方法属于类,它们本质上是“全局的”。在使用它们之前不需要构造任何实例。

public class test1{    public int count=0;    private double radius;    public test1(double radius){        this.radius=radius;        ++count;    }    public static void main(String[] args){        test1 c1=new test1(1.1);        System.out.println("count: "+c1.count);        test1 c2=new test1(2.2);        System.out.println("count: "+c2.count);        test1 c3=new test1(3.3);        System.out.println("count: "+c3.count);    }}
public class test1{    public static int count=0;    private double radius;    public test1(double radius){        this.radius=radius;        ++count;    }    public static void main(String[] args){        test1 c1=new test1(1.1);        System.out.println("count: "+test1.count);        System.out.println("count "+c1.count);        test1 c2=new test1(2.2);        System.out.println("count "+c2.count);        System.out.println("count: "+test1.count);        test1 c3=new test1(3.3);        System.out.println("count "+c3.count);        System.out.println("count: "+test1.count);        System.out.println("count "+c1.count);        System.out.println("count "+c2.count);        System.out.println("count "+c3.count);    }}
  • Output

count: 1
count 1
count 2
count: 2
count 3
count: 3
count 3
count 3
count 3

image-20210610000544871

静态方法只能访问静态变量/方法,它不能访问非静态方法/变量。

但是非静态变量/方法静态和非静态的都可也访问

静态初始值设定项是标记为static的代码块。

加载类时,代码只执行一次。

public class test1{    static int number; //静态变量    static{  //静态变量的初始化代码块,当类加载时,只运行一次。        number=88;        System.out.println("running static initializer");    }    public static void main(String[] args){        System.out.println("running main()...");        System.out.println("number is"+number);    }}
  • Output

running static initializer
running main()…
number is 88

4.6final关键字

final可以声明类、变量或方法

fina类不能被继承(或被扩展)。

final方法不能在子类被重新

final变量不能重新赋值。

原语类型与引用类型的最终变量

原语类型的最终变量是常量,其值不能更改。

public static final double PI = 3.141592653589793;public static final double E = 2.718281828459045;

引用类型的最后一个变量(例如,类或数组的实例)不能重新分配新的引用。

也就是说,您可以修改实例的内容,但不能将变量重新分配给另一个实例。

public class test1{    public static void main(String[] args){        final StringBuffer sb=new StringBuffer("hello");        sb.append(", world");//可以改变引用类型的内容        System.out.println(sb);        //sb=new StringBuffer("world peace"); 报错,不能重新分配        final int[] numbers={11,22,33};//最终变量        numbers[0]=44;        System.out.println(java.util.Arrays.toString(numbers));    }}
  • Output

hello, world
[44, 22, 33]

4.6.2 final vs abstract

abstract final
abstract类必须扩展,实例化扩展类 final类不能被扩展
必须重写abstract方法 不能重写final方法

4.6.3 总结

在java, 类是同类对象的定义。

a class是定义和描述 静态属性 和 动态行为 同一种类的所有物体所共有的。

实例 是 类的特定项的实现。

实例是一个 实例化 一个类的内容。

类的所有实例都具有类似的属性,如类定义中所述 “对象”

4.7 包/引用/路径/JAR

包用于:

  1. 组织类和相关实体。

  2. 管理名称空间-每个包都是一个名称空间。

  3. 解决命名冲突。

例如,com.zzz.Circle和com.yyy.Circle被视为两个不同的类。这两个类可以共存,甚至可以通过完全限定名在同一个程序中使用。

  1. 访问控制:除了public和private之外,您只能将类/变量/方法的访问权授予同一包中的类。

  2. 分发Java类:包中的所有实体都可以组合并压缩成一个文件,称为JAR(Java Archive)文件,用于分发。

4.7.1 Package Naming Convention

包名由域的背面加上您自己组织的项目名(以点分隔)组成。

com.zzz.project1.subproject2子项目

java.lang或java.util

java.net或javax.net。

包名中的“点”对应于用于存储类文件的目录结构。

例如,com.zzz.Cat存储在目录“..\com\zzz\Cat.class”中

com.yyy.project1.subproject2.Orange存储在目录“..\com\yyy\project1\subproject2\Orange.class”中

4.7.2 import 语句

在源代码中引用类有两种方法:
使用packagename.classname形式的完全限定名(例如java.util.Scanner)。

public class ScannerNoImport{    public static void main(String[] args){        java.util.Scanner in = new java.util.Scanner(System.in);        System.out.print("Enter an integer");        int number = in.nextInt();        System.out.println("You have entered "+number);    }}

在源文件的开头添加“import fully qualified name”语句。然后可以在源代码中单独使用类名(不使用包名)。

import java.util.Scanner;public class ScannerWithImport{    public static void main(String[] args){        Scanner in = new Scanner(System.in);        System.out.print("Enter an integer");        int number = in.nextInt();        System.out.println("You have entered "+number);    }}

在JDK1.5中,还可以通过“import static”声明“导入”类的静态变量和方法—可以省略导入的静态变量/方法的类名。

import static java.lang.System.out;import static java.lang.Math.*;public class TestImportStatic{    public static void main(String[] args){        out.println("Hello, PI is"+PI);        out.println("Square root of PI is"+sqrt(PI));    }}

4.7.3 JAR (java Archive)

As an example, the reference “A.b.c().d.e()” can be interpreted as follows:
“A” is a class.
“b” is a public static variable of class “A”
The variable “b” belongs to a class say “X”.
The class “X” provides a public method “c()”.
The “c()” method returns an instance “y” of class say “Y”.
The “Y” class has a variable (static or instance) called “d”.
The variable “d” belongs to a class say “Z”.
The class “Z” provides a public method called “e()”.

image-20210610004832979

4.8 变量类型

The type of a variable determines what kinds of value the variable can hold and what operations can be performed on the variable.
Primitive type: byte, short, int, long, float, double, char, and boolean.
Reference type: Reference types include class, interface, enum and array.
A special null type, holding a special null reference.

image-20210610004949662

4.8.1 变量作用域和生存期

变量的作用域是指可以访问变量的代码部分。

自动变量(或局部变量)

类的成员变量(或实例变量)

类的静态变量(或类变量)

生存期是指变量在内存中创建到被销毁(垃圾收集)的时间跨度。

4.9 方法重写/重装

  • 方法重写:
  1. 必须与原始参数列表相同。

  2. 必须具有与其原始返回类型相同的返回类型或子类型

  3. 访问修饰符的限制性不能比原始的多,但限制性可以更小,例如,可以将受保护的方法重写为公共方法。

  4. 重写私有方法没有意义,因为私有方法并不是由它的子类真正继承的。

  5. 从技术上讲,子类不会覆盖静态方法,而只是隐藏它。

  6. 无法重写final方法。抽象方法必须在实现子类中重写(否则,子类仍然是抽象的)。

  • 方法重装:
  1. 必须通过其参数列表进行区分。不应根据返回类型、异常列表或访问修饰符(生成编译错误)进行区分。

  2. 可以存在于原始类或其子类中。

4.10 jdk api中常用的包

JDK API is huge and consists of many packages:

  • java.lang (the core JDK package): contains classes that are core to the language, e.g., System, String, Math, Object, Integer, and etc.
  • java.util: contains utilities such as Scanner, Random, Date, ArrayList, Vector, Hashtable.
  • java.io: contains input and output classes for reading files and I/O streams, such as File.
  • java.net: contains networking support, such as Socket and URL.
  • java.awt (Abstract Windowing Toolkit): contains classes for implementing a graphical user interface, including classes like Frame, Button, CheckBox.
  • java.awt.event: contains event handling classes, such as key-press, mouse-click etc.
  • java.swing: Advanced GUI classes, e.g., JFrame, JButton, JApplet, etc.
  • java.sql: contains classes for database programming, such as Connection, Statement, ResultSet.

4.11 基本类型的包装类

Java语言的设计者保留了面向对象语言中的原语类型。

JDK提供了所谓的包装器类,这些包装器类将八种原语类型中的每一种原语值包装到对象中。

image-20210610005608963

每个包装器类都有一个构造函数,该构造函数接受它包装的数据类型。

Integer aIntObj=new Integer(5566);//Wrap a double primitive value into a Double objectDouble aDoubleObj=new Double(55.66);//Wrap a char primitive value into a Character objectCharacter aCharObj=new Character('z');////Wrap a Boolean primitive value into a Boolean objectBoolean aBooleanObj=new Boolean(true);

抽象超类号定义了要展开的以下xxxValue()方法,这些方法在具体的子类Byte、Short、Integer、Long、Float和Double中实现。换句话说,您可以从Integer对象获得int或double值。

Integer intObj=new Integer(56677);int i=intObj.intValue();short s=intObj.shortVaue();//truncatebyte b=intObj.byteValue();//truncateDOuble doubleObj=new Double(55.66);double d=doubleObj.doubleValue();int il=doubleObj.intValue();//truncateBoolean booleanObj=new Boolean(false);//Unwrapboolean b1=booleanObj.booleanValue();

4.12 autoboxing

JDK1.5引入了一个称为自动装箱和拆箱的新特性,编译器可以根据上下文自动为您进行包装和拆箱。

通过自动装箱和拆箱,您实际上可以忽略原语与其包装器对象之间的区别。

//Java SE 5.0Integer intObj=5566;//autobox from int to Integerint i=intObk;//auto-unbox from Integer to intDouble doubleObj=55.66;//autobox from double to Doubledouble d=doubleObj;//auto-unbox from Double to double

5. 字符串&枚举类型

class_8_string_enum.pptx

5.1 字符串

文本字符串类型具有以下特征:

  • 不是原始数据类型;是一个类

  • 用双引号(“”)括起来

String greeting ="Good Morning!\n";String errorMessage="Record Not Found";String str1,str2;String s1=new String("Hello");

5.1.1 字符串的方法

int length()       // returns the length of the Stringboolean isEmpty()  // same as str.length() == 0boolean equals(String another) // CANNOT use '==' or '!=' to compare two Strings in Javaboolean equalsIgnoreCase(String another)int compareTo(String another)  // return 0 if this string is the same as another;boolean startsWith(String another)boolean startsWith(String another, int fromIdx)  // search begins at fromIdxboolean endsWith(String another)int indexOf(String key)int indexOf(String key, int fromIdx)int indexOf(int char)int indexOf(int char, int fromIdx)      // search forward starting at fromIdxchar charAt(int idx)String substring(int fromIdx)String toLowerCase()String toUpperCase()String concat(String another)  // same as str+anotherchar[] toCharArray() boolean matches(String regex)String replace(char old, char new)
  • Java字符串是Java.lang.String类的对象。

  • 然而,Java字符串是特殊的。

  • 字符串以双引号文本的形式与字符串文本相关联,例如“hello,world”。

  • “+”运算符重载以连接两个字符串操作数。

  • 字符串是不可变的。也就是说,它的内容一旦被创建就不能被修改。

  • 字符串在Java中受到特殊处理,因为它们在程序中经常使用。

  • 原语存储在方法栈中,方法栈需要较少的存储空间,并且操作成本较低。

  • 对象存储在程序堆中,这需要复杂的内存管理和更多的存储空间。

  • 出于性能考虑,Java的字符串被设计为介于原语和对象之间。

  • 字符串的特殊功能包括:

  1. “+”运算符对原语(如int和double)执行加法,重载后可对字符串对象进行操作对两个字符串操作数执行串联。

    出于软件工程的考虑,Java不支持操作符重载。

    请注意,“+”不适用于任意两个对象,例如点或圆。

  2. 字符串可以通过以下方式构造:

    直接将字符串文字赋给字符串引用-就像原语一样

    通过“new”操作符和构造函数,类似于任何其他类。

  3. 字符串文本存储在公共池中。通过new操作符分配的字符串对象存储在堆中

String s1="Hello";//String literalString s2="Hello";//String literalString s3=s1; //same referenceString s4=new String("Hello");//String ObjectString s5=new String("Hello");//String Object

image-20210610220036743

public class test1{    public static void main(String[] args){        String m_kobe="1";        String[] m_king={"2"};        mb_oprate(m_kobe,m_king);        System.out.println(m_kobe+m_king[0]);    }    public static void mb_oprate(String kobe,String[] king){        kobe=new String("3");        king[0]=new String("4");    }}
  • Output

14

5.1.2 字符串是不可变的

由于具有相同内容的字符串文本共享公共池中的存储,因此Java的字符串被设计为不可变的。

也就是说,一旦构造了字符串,就不能修改其内容。否则,共享同一存储位置的其他字符串引用将受到更改的影响,这可能是不可预测的,因此是不可取的。

public class test1{    public static void main(String[] args){        String str="Hello";        for(int i=1;i<10;i++){            str=str+i;            System.out.println(str);        }    }}
  • Output

Hello123
Hello1234
Hello12345
Hello123456
Hello1234567
Hello12345678
Hello123456789

5.2 StringBuffer&StringBuilder

  • JDK提供了两个类来支持可变字符串:StringBuffer和StringBuilder

(在核心包java.lang中)。

  • StringBuffer或StringBuilder对象与任何普通对象一样,它们存储在堆中,不共享,因此可以修改,而不会对其他对象造成不利的副作用。

  • StringBuilder类是在JDK5中引入的。StringBuilder对于多线程操作不同步。

  • 然而,对于单线程程序,StringBuilder没有同步开销,效率更高。

//java.lang.StringBufferint length()StringBuffer append(type arg) StringBuffer insert(int offset, arg)StringBuffer delete(int fromIdx, int toIdx)StringBuffer deleteCharAt(int idx)void setLength(int newSize)void setCharAt(int idx, char newChar)StringBuffer replace(int fromIdx, int toIdx, String s)StringBuffer reverse()char charAt(int idx)String substring(int fromIdx)String substring(int fromIdx, int toIdx)String toString()int indexOf(String key)int indexOf(String key, int fromIdx)int lastIndexOf(String key)int lastIndexOf(String key, int fromIdx)
public class test1{    public static void main(String[] args){        int year=2010,month=10,day=10;        int hour=10,minute=10,second=10;        String dateStr=new StringBuilder().append(year).append("-").append(month).append("-").append(day).append(" ").append(hour).append(":").append(minute).append(":").append(second).toString();        System.out.println(dateStr);        String anotherDataStr=year+"-"+month+"-"+day+" "+hour+":"+minute+":"+second;        System.out.println(anotherDataStr);    }}
  • Output

2010-10-10 10:10:10
2010-10-10 10:10:10

  • 经验法则

如果不修改字符串(因为它们在字符串公共池中共享),那么它们的效率会更高。

但是,如果必须经常修改字符串的内容(如状态消息),则应改用StringBuffer类(或下面描述的StringBuilder)。

StringBuilder的API与StringBuffer类兼容。

public class test1{    public static void main(String[] args){        long beginTime,elapsedTime;        String str="";        int size=16536;        char ch='a';        beginTime=System.nanoTime();        for(int count=0;count<size;++count){            str+=ch;            ++ch;            if(ch>'z'){                ch='a';            }        }        elapsedTime=System.nanoTime()-beginTime;        System.out.println("Elapsed Time is:"+elapsedTime/1000+"usec(Build String");        String strReverse="";        beginTime=System.nanoTime();        for(int pos=str.length()-1;pos>=0;pos--){            strReverse+=str.charAt(pos);//Concatenate        }        elapsedTime=System.nanoTime()-beginTime;        System.out.println("Elapsed Time is"+elapsedTime/1000+"usec(Using String to reverse");        beginTime=System.nanoTime();        StringBuffer sBufferReverse=new StringBuffer(size);        for(int pos=str.length()-1;pos>=0;pos--){            sBufferReverse.append(str.charAt(pos));        }        elapsedTime=System.nanoTime()-beginTime;        System.out.println("Elapsed Time is "+elapsedTime/1000+"usec(Using StringBuffer to reverse");        beginTime=System.nanoTime();        StringBuffer stringBufferReverseMethod=new StringBuffer(str);        stringBufferReverseMethod.reverse();        elapsedTime=System.nanoTime()-beginTime;        System.out.println("Elapsed Time is"+elapsedTime/1000+" usec (Using Stringbuffer's reberse() method");        beginTime=System.nanoTime();        StringBuffer sBuilderReverse=new StringBuffer(size);        for(int pos=str.length()-1;pos>=0;pos--){            sBuilderReverse.append(str.charAt(pos));        }        elapsedTime=System.nanoTime()-beginTime;        System.out.println("Elapsed Time is "+elapsedTime/1000+" usec (Using StringBuilder to reverse)");        beginTime=System.nanoTime();        StringBuffer sBuilderReverseMethod=new StringBuffer(str);        sBuilderReverseMethod.reverse();        elapsedTime=System.nanoTime()-beginTime;        System.out.println("Elapsed Time is "+elapsedTime/1000+ " usec(Using StringBuilder's reverse())");    }
  • Output

Elapsed Time is:447360usec(Build String
Elapsed Time is415946usec(Using String to reverse
Elapsed Time is 1171usec(Using StringBuffer to reverse
Elapsed Time is821 usec (Using Stringbuffer’s reberse() method
Elapsed Time is 1022 usec (Using StringBuilder to reverse)
Elapsed Time is 522 usec(Using StringBuilder’s reverse())

5.2 枚举

枚举是一种特殊的类,因此你需要保存这个公共的枚举并命名为“EnumName.java”.

/*enum{ITEM1,ITEM2,..;}*/public enum HandSign{    SCISSOR,PAPER,STONE;}

枚举是一种特殊类型,它提供程序中常量的类型安全实现。

假设我们正在写一个剪刀纸石游戏。我们可以使用三个任意整数(例如,0,1,2;或88、128、168),三个字符串(“剪刀”、“纸”、“石头”)或三个字符(“s”、“p”、“t”)代表三个手势。

import java.util.Scanner;import java.util.ArrayList; // 引入 ArrayList 类import java.util.Random;import java.text.DecimalFormat;import java.text.NumberFormat;import java.util.*;enum HandSign{    SCISSOR,PAPER,STONE;}public class test1{    public static void main(String[] args){        Random random=new Random();        boolean gameover=false;        HandSign playerMover=HandSign.SCISSOR;        HandSign computeMove;        int numTrails=0;        int numComputerWon=0,numPlayerWon=0,numTie=0;        Scanner in=new Scanner(System.in);        System.out.println("Let us begin");        while(!gameover){            System.out.println("Scissor-paprt-stone");            boolean validInput;            do{                System.out.println(" show me your sign(Enter s for scissor, p for paper, t for stone, q to quit");                char inchar=in.next().toLowerCase().charAt(0);                validInput=true;                switch (inchar){                    case 'q':gameover=true;break;                    case 's':playerMover=HandSign.SCISSOR;break;                    case 'p':playerMover=HandSign.PAPER;break;                    case 't':playerMover=HandSign.STONE;break;                    default:                        System.out.println(" Invalid input, try again");                        validInput=false;                }            }while (!validInput);            if(!gameover){                int rand=random.nextInt(3);                computeMove=HandSign.values()[rand];                System.out.println("My Sign is "+computeMove);                if(computeMove==playerMover){                    System.out.println(" Tie!");                    ++numTie;                }else if(computeMove==HandSign.SCISSOR&&playerMover==HandSign.PAPER){                    System.out.println(" Scissor cuts paper, I won");                    ++numComputerWon;                }else if(computeMove==HandSign.PAPER&&playerMover==HandSign.STONE){                    System.out.println(" Paper wraps stone, I won!");                    ++numComputerWon;                }else if(computeMove==HandSign.STONE&&playerMover==HandSign.SCISSOR){                    System.out.println("Stonr breals Scissorr, I won");                    ++numComputerWon;                }else {                    System.out.println(" You won!");                    ++numPlayerWon;                }                ++numTrails;            }        }    }}

枚举是一种引用类型(就像类、接口和数组一样),它保存对堆中内存的引用。

它是隐式的final,因为常量不应更改。

它可以包含传统类的其他组件,例如构造函数、成员变量和方法。

属性:

  1. 枚举是类型安全的!
  2. 枚举提供它们的命名空间。
  3. 无论何时定义枚举,都会创建一个扩展java.lang.enum的类。因此,enum不能扩展另一个类或enum。编译器还为枚举中定义的每个常量创建类的实例。java.lang.Enum有以下方法
  4. 枚举中定义的所有常量都是公共静态final。因为它们是静态的,所以可以通过EnumName.instanceName访问它们。
  5. 不实例化枚举,而是依赖定义的常量。
  6. 枚举可以在switch case语句中使用,就像int一样。

6. 集合框架

  • 尽管我们可以使用数组作为容器来存储一组相同类型的元素(原语或对象)。但是,数组不支持所谓的动态分配—它有一个固定长度,一旦分配就不能更改。

  • 此外,阵列是一种简单的线性结构。许多应用程序可能需要更复杂的数据结构,如链表、堆栈、哈希表、集合或树

  • 在Java中,动态分配的数据结构(如ArrayList、LinkedList、Vector、Stack、HashSet、HashMap、Hashtable)在一个称为Collection Framework的统一体系结构中得到支持。

  • 集合只是一个容器对象,它包含一个对象集合。

  • 集合框架提供了一个统一的接口来存储、检索和操作集合的元素,而不考虑底层的实际实现。

    Java集合框架包(Java.util)包含:

  1. 一套接口

  2. 实现类,以及

  3. 算法(如排序和搜索)。

    类似于C++标准模板库(STL)。

import java.util.List;import java.util.ArrayList;import java.util.Iterator;import java.util.Locale;public class test1{    public static void main(String[] args){        List<String> coffeeLst=new ArrayList<>();        coffeeLst.add("espresso");        coffeeLst.add("latte");        coffeeLst.add("cappuccino");        System.out.println(coffeeLst);        Iterator<String> iter = coffeeLst.iterator();        while(iter.hasNext()){            String str=iter.next();            System.out.println(str);        }        for(String str:coffeeLst)System.out.println(str.toUpperCase(Locale.ROOT));        for(int i=0;i<coffeeLst.size();++i){            System.out.println(coffeeLst.get(i).substring(0,3));        }     //   coffeeLst.add(new Integer(1234));       // Integer intObj=coffeeLst.get(0);    }}
  • Output

[espresso, latte, cappuccino]
espresso
latte
cappuccino
ESPRESSO
LATTE
CAPPUCCINO
esp
lat
cap

第1-3行导入集合框架类和接口驻留在java.util包中。

在第7行中,我们构造了一个ArrayList实例,并将其升级到List接口。

这是可能的,因为ArrayList是List的子类型。

记住,一个好的程序是在规范上运行,而不是实际实现

ListcoffeeLst=新ArrayList()

第8-10行将元素(实例化的实际类型字符串)添加到集合中

遍历集合元素的方法有三种:

通过关联迭代器(第13-17行)

在JDK5(第19-20行)中引入的每个循环使用新的

在JDK 8中引入的流上使用聚合操作。(第22-24行)

第25-26行是编译的时间类型安全型。

  • 集合框架中接口和常用实现类的层次结构

image-20210611095326678

6.1. 泛型

public class test1{    private int size;   //number of elements    private Object[] elements;   //can store all Java objects    public test1(){        elements=new Object[10];//allocate initial capacity of 10        size=0;    }    public void add(Object o){        if(size>=elements.length){            Object[] newElements=new Object[size+10];            for(int i=0;i<size;++i)newElements[i]=elements[i];        }        elements[size]=o;        ++size;    }    public Object get(int index){        if(index>=size)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);        return elements[index];    }    public int size(){        return size;}    public static void main(String[] args){        test1 strLst=new test1();        strLst.add("alpha");        strLst.add("belta");        System.out.println(strLst);        for (int i=0;i< strLst.size();i++){            String str=(String) strLst.get(i);            System.out.println(str);        }        strLst.add(new Integer(1234));        String str=(String)strLst.get(2);//报错:java.lang.Integer cannot be cast to java.lang.String    }}
  • Output

test.test1@4b67cf4d
alpha
belta

public class test1{    public static void main(String[] args){        ArrayList<String> fruitlst=new ArrayList<String>();        fruitlst.add("apple");        fruitlst.add("orange");        System.out.println(fruitlst);        for(String str:fruitlst){            System.out.println(str);        }        // fruitlst.add(99); 报错        List<String> animalLst=new ArrayList<>();        animalLst.add("tiger");        System.out.println(animalLst);        List<Integer> intLst=new ArrayList<>();        intLst.add(11);        int i1=intLst.get(0);        System.out.println(intLst);        //intLst.add(2.2); 报错        List<Number> numLst=new ArrayList<>();        numLst.add(33);        numLst.add(4.4);        System.out.println(numLst);    }}
  • Output

[apple, orange]
apple
orange
[tiger]
[11]
[33, 4.4]

The is called the formal “type” parameter for passing type information into the generic class.
During instantiation, the formal type parameters are replaced by the actual type parameters.
for an element of a collection;
<K, V> for key and value.
for number
for type
S,U,V, etc. for 2nd, 3rd, 4th type parameters
Generic Wildcard (?)
<?>: called unbounded wildcard which accepts all types.
Bounded Type

<? extends T>
<? super T>

public class test1<E>{    private E content;    public test1(E content){        this.content=content;    }    public  E getContent(){        return content;    }    public void setContent(E content){        this.content=content;    }    public String toString(){        return "GenericBox[content="+content+"("+content.getClass()+")]";    }    public static void main(String[] args){    }}

6.1.1 例子

class BoxPrinter<T>{    private T val;    public BoxPrinter(T arg){        val=arg;    }    public String toString(){        return "["+val+"]";    }}public class test1{    public static void main(String []args){        BoxPrinter<Integer> value1=new BoxPrinter<Integer>(new Integer(10));        System.out.println(value1);        BoxPrinter<String>value2=new BoxPrinter<String>("Hello world");        System.out.println(value2);    }}
  • Output

[10]
[Hello world]

class Pair<T1,T2>{    T1 object1;    T2 object2;    Pair(T1 one,T2 two){        object1=one;        object2=two;    }    public T1 getFirst(){        return object1;    }    public T2 getSecond(){        return object2;    }}public class test1{    public static void main(String []args){        Pair<Integer,String>worldCup=new Pair<Integer,String>(2018,"Russia");        System.out.println("World cup "+worldCup.getFirst()+" in "+worldCup.getSecond());    }}
  • Output

World cup 2018 in Russia

public class test1{    public static void main(String []args){      List<Number>numLst=new ArrayList<>();      numLst.add(1.1f);      System.out.println(numLst);        Collection<Integer>intColl=new LinkedList<>();        intColl.add(2);        intColl.add(3);        System.out.println(intColl);//[2,3]        numLst.addAll(intColl);        System.out.println(numLst);        Set<Double> numSet=new HashSet<>();        numSet.add(4.4);        numSet.add(5.5);        System.out.println(numSet);        numLst.addAll(numSet);        System.out.println(numLst);    }}
  • Output

[1.1]
[2, 3]
[1.1, 2, 3]
[5.5, 4.4]
[1.1, 2, 3, 5.5, 4.4]

6.2 迭代器 Iterable<E>

There are three ways to traverse through all the elements of a Collection:

  1. Via the associated Iterator object retrieved from the super-type Iterable
  2. Using the for-each loop (introduced in JDK 5)
  3. Via the Stream API

java.lang.Iterable interface
one abstract method called iterator()
abstract Iterator iterator();
abstract boolean hasNext()
abstract E next()
abstract void remove()

import java.util.List;import java.util.ArrayList;import java.util.Iterator;import java.util.Locale;public class test1{    public static void main(String[] args){        List<String> coffeeLst=new ArrayList<>();        coffeeLst.add("espresso");        coffeeLst.add("latte");        coffeeLst.add("cappuccino");        System.out.println(coffeeLst);        Iterator<String> iter = coffeeLst.iterator();        while(iter.hasNext()){            String str=iter.next();            System.out.println(str);        }        for(String str:coffeeLst)System.out.println(str.toUpperCase(Locale.ROOT));        for(int i=0;i<coffeeLst.size();++i){            System.out.println(coffeeLst.get(i).substring(0,3));        }     //   coffeeLst.add(new Integer(1234));       // Integer intObj=coffeeLst.get(0);    }}

6.2.1 for-each vs Iterator

  1. for-loop provides a convenience way to traverse through a collection of elements.
  2. for-loop hides the Iterator
  3. Iterator would remove or replace the elements.
  4. the loop variable receives a “cloned” copy of the object reference.
import java.util.*;import java.util.ArrayList;import java.util.Iterator;public class test1{    public static void main(String []args){        List<StringBuilder> lst=new ArrayList<>();        lst.add(new StringBuilder("alpha"));        lst.add(new StringBuilder("Delta"));        lst.add(new StringBuilder("charlie"));        System.out.println(lst);        for (StringBuilder sb:lst){          sb.append("123");        }        System.out.println(lst);    }}
  • Output

[alpha, Delta, charlie]
[alpha123, Delta123, charlie123]

public class test1{    public static void main(String []args){        List<String> lst=new ArrayList<>();        lst.add("alpha");        lst.add("Delta");        lst.add("charlie");        System.out.println(lst);        for (String str:lst){          str+="change!";        }        System.out.println(lst);    }}
  • Output

[alpha, Delta, charlie]
[alpha, Delta, charlie]

import java.util.ArrayList;import java.util.Iterator;public class test1{    public static void main(String []args){        ArrayList<Integer>nums=new ArrayList<Integer>();        for(int i=1;i<10;i++)nums.add(i);        System.out.println("Original list "+nums);        Iterator<Integer>numsIter = nums.iterator();        while (numsIter.hasNext()){          numsIter.remove();        }        System.out.println("List after removing all elements "+ nums);    }}

6. 3 集合接口

It defines the common behaviors expected of all classes

abstract int size();abstract boolean isEmpty();    abstract boolean add(E element);abstract boolean remove(Object element);abstract boolean contains(Object element);abstract void clear();abstract boolean addAll(Collection<? extends E> c);abstract boolean containsAll(Collection<?> c);abstract boolean removeAll(Collection<?> c);abstract boolean retainAll(Collection<?> c);abstract boolean equals(Object o);abstract int hashCode();abstract Object[] to Array();abstract <T> T[] toArray(T[] a);

6.3.1. Sub-interface of Collection<E>

  • 3 sub-interface
    List: models a resizable linear array, which allows numerical indexed access, with index starting from 0.
    Set: models a mathematical set, where no duplicate elements are allowed.
    Queue: models queues such as First-in-First-out (FIFO) queue and priority queue.
  • a Map contains a collection of key-value pairs.
    The interface Map<K,V>, which takes two generic types K and V, as a collection of “key-value pairs”. No duplicate key is allowed.

6.3.1.1. List

  1. A List models a resizable linear array, which supports numerical indexed access, with index starts from 0.
  2. Elements in a list can be retrieved and inserted at a specific index position based on an int index.
  3. It can contain duplicate elements.

image-20210611111651970

abstract Iterator<E> iterator();abstract int size();abstract boolean isEmpty();abstract boolean add(E element);abstract boolean remove(Object obj);abstract boolean contains(Object obj);abstract void clear();abstract void add(int index,E element);abstract E set(int index,E element);abstract E get(int index);abstract E remove(int index);abstract int indexOf(Object obj);abstract int lastIndexOf(Object obj);abstract List<E> subList(int fromIndex,int toIndex);
//List algorithmstatic void swap(List<?> lst,int i,int j);static void shuffle(List<?> lst);static void shuffle(List<?> lst,Ramdon rand);static void reverse(List<?> lst);static void rotate(List<?> lst,int distance);static <T> void fill(List<? super T>,T ,obj);static <T> void copy(List<?super T>dest,List<?extends T> src);static<T> boolean replaceAll(List<T> lst,T oldVal,T newVal);List<E> subList(int fromIdx,int toIdx)
import java.util.*;import java.util.ArrayList;import java.util.Iterator;public class test1{    public static void main(String []args){        List<Integer> lst=new ArrayList<>();        for(int i=0;i<10;++i)lst.add(i*10);        System.out.println(lst);                lst.subList(3,6).clear();        System.out.println(lst);                System.out.println(lst.subList(2,5).indexOf(60));        System.out.println(Collection.indexOfSubList(lst,lst2));        System.out.println(Collection.lasIndexOfSubList(lst,lst2));    }}

image-20210611113314639

6.3.1.2 Set<E>Interfaces

The Set interface models a mathematical set, where no duplicate elements are allowed (e.g., playing cards).

image-20210611123238321

public class test1{  private int id;  private String title;  public test1(int id,String title){    this.id=id;    this.title=title;  }  @Override  public String toString(){    return id+": "+title;  }  @Override  public int hashCode(){    return id;  }  public static void main(String []args){        test1 book1=new test1(1,"Java for Dummies");        test1 book1Dup=new test1(1,"Java for the Dummies");        test1 book2=new test1(2,"Java for more Dummies");        test1 book3=new test1(3,"more Java for more Dummies");        Set<test1> set=new LinkedHashSet<test1>();        set.add(book1);        set.add(book1Dup);        set.add(book1);        set.add(book3);        set.add(null);        set.add(null);        set.add(book2);        System.out.println(set);    }}
  • Output

[1: Java for Dummies, 1: Java for the Dummies, 3: more Java for more Dummies, null, 2: Java for more Dummies]

6.3.1.3 SortedSet<E> and NavigableSet<E> Interfaces

The NavigableSet<E> is a sub-interface of SortedSet<E>

image-20210611125929988

public class test1 implements Comparable<test1>{  private String name,address,phone;  public test1(String name){    this.name=name;  }  @Override  public String toString(){    return name;  }  @Override  public int compareTo(test1 other){    return this.name.compareToIgnoreCase(other.name);  }  @Override  public int hashCode(){    return name.toLowerCase(Locale.ROOT).hashCode();  }  public static void main(String []args){        test1 addr1=new test1("peter");        test1 addr2=new test1("paul");        test1 addr3=new test1("patrick");        TreeSet<test1> set = new TreeSet<>();        set.add(addr1);        set.add(addr2);        set.add(addr3);        System.out.println(set);        System.out.println(set.floor(addr2));        System.out.println(set.lower(addr2));        System.out.println(set.tailSet(addr2));  }}
  • Output

[patrick, paul, peter]
paul
patrick
[paul, peter]

public class test1{    public static void main(String []args){        String pangram ="the quick brown fox jumps over the lazy dog";        Set<Character> aToZee=new TreeSet<Character>();        for(char gram:pangram.toCharArray())aToZee.add(gram);        System.out.println("The pangram is: "+pangram);        System.out.print("Sorted pangram character are: "+aToZee);  }}
  • Output

The pangram is: the quick brown fox jumps over the lazy dog
Sorted pangram character are: [ , a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]

6.4 队列 Queue<E>

队列是一个集合,其元素是按特定顺序添加和删除的,通常是以先进先出(FIFO)的方式。

image-20210611144612382

6.5 Map<K,V>

映射是键值对的集合。每个键只映射一个值。不允许重复键,但允许重复值。

image-20210611144751454

Map<K,V>接口的实现包括:

  1. HashMap<K,V>:Map<K,V>接口的哈希表实现。最好的全方位实施。HashMap中的方法不同步。

  2. TreeMap<K,V>:SortedMap<K,V>接口的红黑树实现。

  3. LinkedHashMap<K,V>:带有链接列表的哈希表,便于插入和删除。

  4. 哈希表:改进的遗留(JDK1.0)实现。Map<K,V>接口的一种同步哈希表实现,不允许使用旧方法使用空键或空值。

abstract int size();abstract boolean isEmpty();abstract V get(Object key);abstract B put(K key,V value);abstract boolean contanKey(Object key);abstract boolean containValue(Object value);abstract void clear();abstract void V remove(Object key);abstract Set<K>keySet();abstract Collection<V> values();abstract Set<Map,Entry<K,V>> entrySet();

6.5.1 例子

public class test1{    public static void main(String []args){       Map<String,Double> map=new HashMap<>();       map.put("espresso",1.1);       map.put("latte",2.2);       map.put("cappuccino",3.3);       System.out.println(map);       for(Map.Entry<String,Double>e:map.entrySet()){         e.setValue(e.getValue()+10.0);         System.out.println(e.getKey()+":"+e.getValue());       }       System.out.println(map.keySet());       for(String key:map.keySet()){         System.out.println(key+"="+map.get(key));       }       Iterator<String> iter=map.keySet().iterator();       while(iter.hasNext()){         String key=iter.next();         System.out.println(key+":"+map.get(key));       }       System.out.println(map.values());  }}
  • Output

{espresso=1.1, cappuccino=3.3, latte=2.2}
espresso:11.1
cappuccino:13.3
latte:12.2
[espresso, cappuccino, latte]
espresso=11.1
cappuccino=13.3
latte=12.2
espresso:11.1
cappuccino:13.3
latte:12.2
[11.1, 13.3, 12.2]

public class test1{    public static void main(String []args) throws FileNotFoundException {       Scanner in=new Scanner(new File(args[0]));       Map<String,Integer>map=new HashMap<>();       while(in.hasNext()){         String word=in.next();         int freq=(map.get(word)==null)?1:map.get(word)+1;         map.put(word,freq);       }       System.out.println(map);  }}
public class test1{    public static void main(String []args) throws FileNotFoundException {       NavigableMap<Integer,String>examScores=new TreeMap<Integer,String>();       examScores.put(90,"Sophia");       examScores.put(20,"Isabella");       examScores.put(10,"Emma");       examScores.put(50,"Olivea");       System.out.println("The data in the map is:"+examScores); //排序后的       System.out.println("The data descending order is: "+examScores.descendingKeySet());       System.out.println("Drtails of those who passed the exam: "+examScores.tailMap(40));//.tailMap():value高于x的       System.out.println("The lowset mark is: "+examScores.firstEntry());  }}
  • Output

The data in the map is:{10=Emma, 20=Isabella, 50=Olivea, 90=Sophia}
The data descending order is: [90, 50, 20, 10]
Drtails of those who passed the exam: {50=Olivea, 90=Sophia}
The lowset mark is: 10=Emma

6.6 Compare

Comparable Interface
Comparator Class

6.6.1 Searching,Sorting and Ordering

static<T> int binarySearch(List<? extends T> lst, T key,Comparator<?super T>comp);static<T> int binarySearch(List<? extends Comparable<?super T> lst, T key);static<T> void sort(List<T>lst, Comparator<? super T> comp);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 max(Collection<? extends T> coll);static<T>T min(Collection<? extends T>coll,Comparator<? super T>comp);static<T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll);

在这两种情况下需要排序:

要对集合或数组进行排序(使用Collections.sort()或Arrays.sort()方法),需要一个排序规范。

一些集合,特别是SortedSet(TreeSet)和SortMap(TreeMap)是被排序的。

有两种方法可以指定对象的顺序:

1.使对象实现java.lang.Comparable接口,并重写compareTo()方法来指定比较两个对象的顺序。

2.创建一个特殊的java.util.Comparator对象,使用compare()方法指定比较两个对象的顺序。

6.6.1.1 Comparable<T>

A java.lang.Comparable interface 确定了为了排序两个对象是如何被比较的,它定义了一个抽象类型:

CompareTo(T o)

返回1如果 当前对象>被传递的对象

返回0如果 当前对象==被传递的对象

返回-1如果当前对象小于被传递的对象

public class test1{    public static void main(String []args) throws FileNotFoundException {       String[] strArray={"Hello","hello","Hi","HI","Hello"};       Arrays.sort(strArray);       for(String st:strArray)       System.out.println(st);       System.out.println(Arrays.binarySearch(strArray,"Hello"));       System.out.println(Arrays.binarySearch(strArray,"Hello"));       List<Integer> lst=new ArrayList<>();       lst.add(22);       lst.add(11);       lst.add(44);       lst.add(11);       Collections.sort(lst);       System.out.println(lst);       System.out.println(Collections.binarySearch(lst,22));       System.out.println(Collections.binarySearch(lst,35));  }}
  • Output

HI
Hello
Hello
Hi
hello
2
2
[11, 11, 22, 44]
2
-4

public class ComparablePerson extends Person implements Comparable<Person>{    public ComparablePerson(String name,int ages){        super(name,age);    }    @Override    public int compareTo(Person p){        return this.getName().compareToIgnoreCase(p.getName());    }}public class ComparablePersonTest{    public static void main(String[] args){        List<ComparablePerson>plst=new ArrayList<>();        plst.add(ComparablePerson("peter",21));        plst.add(ComparablePerson("Paul",18));        plst.add(ComparablePerson("John",60));        System.out.println(plst);                Collections.sort(plst);        System.out.printlm(plst);                System.out.println(Collections.binarySearch(plst,new ComparablePerson("PAUL",18)));        System.out.println(Collections.binarySearch(plsr,new ComparablePerson("PAUL",16)));        System.out.println(collections.binarySearch(plst, new ComparablePerson("Kelly",18)));    }}

6.6.1.2 Comparator<T>Interface

compare(T o1,To2)

return 1,o1>o2

return 0, o1==o2

return -1,o1<o2

Comparator<T>接口声明了一个抽象方法(在jdk8中称为函数接口):

Comparator<T>:下列任何一种方式:

1.命名的内部类

2.匿名内部类

3.Lambda表达式(JDK 8)

public class test1{    public static class StringComparator implements Comparator<String>{        @Override        public int compare(String s1,String s2){            return s1.compareToIgnoreCase(s2);        }    }    public static void main(String []args){    Comparator<String> strComp=new StringComparator();    String[] array={"Hello","Hi","HI","hello","Hello"};    Arrays.sort(array,strComp);    System.out.println(Arrays.toString(array));    System.out.println(Arrays.binarySearch(array,"Hello",strComp));    System.out.println(Arrays.binarySearch(array,"HELLO",strComp));  }}
  • Output

[Hello, hello, Hello, Hi, HI]
2
2

public class test1{    /*    public static class StringComparator implements Comparator<String>{        @Override        public int compare(String s1,String s2){            return s1.compareToIgnoreCase(s2);        }    }     */    public static void main(String []args){    Comparator<Integer> intComp=new Comparator<Integer>(){        @Override        public int compare(Integer i1,Integer i2){            return i1*10-i2*10;        }    };    List<Integer> lst=new ArrayList<Integer>();    lst.add(42);    lst.add(21);    lst.add(34);    lst.add(13);    Collections.sort(lst,intComp);    System.out.println(lst);    System.out.println(Collections.binarySearch(lst,22,intComp));    System.out.println(Collections.binarySearch(lst,35,intComp));  }}
  • Output

[13, 21, 34, 42]
-3
-4

public class test1{    public static void main(String []args){    Comparator<String> strComp=(s1,s2)->s1.compareToIgnoreCase(s2);    String[] array={"Hello","Hi","HI","hello","Hello"};    Arrays.sort(array,strComp);    System.out.println(Arrays.toString(array));    System.out.println(Arrays.binarySearch(array,"Hello",strComp));    System.out.println(Arrays.binarySearch(array,"HELLO",strComp));    Comparator<Integer> intComp =(i1,i2)->i1%10-i2%10;    List<Integer>lst=new ArrayList<Integer>();    lst.add(42);    lst.add(21);    lst.add(34);    lst.add(13);    Collections.sort(lst,intComp);    System.out.println(lst);    System.out.println(Collections.binarySearch(lst,22,intComp));    System.out.println(Collections.binarySearch(lst,35,intComp));    }}
  • Output

[Hello, hello, Hello, Hi, HI]
2
2
[21, 42, 13, 34]
1
-5

Process finished with exit code 0

7.exception

class_10_exception.pptx

7.1 异常处理 Exceoption Handing

  • 异常是在程序执行期间发生的异常事件,它会中断程序的正常流程。

您可能希望用户输入一个整数,但收到一个文本字符串;

运行时会弹出意外的I/O错误。

网络连接

数据库连接

文件可能保持打开状态

数据库和文件记录可能处于不一致的状态。

如果这些异常处理不当,程序会突然终止,并可能导致严重后果。

  • Java将异常处理构建到语言中:

将通知您调用方法时可能出现的异常情况—异常在方法的签名中声明。

在编写主逻辑时,您必须处理异常,不能把它们作为事后的考虑——没有异常处理代码,您的程序就无法编译。

异常处理代码通过try-catch-finally构造与主逻辑分离。

注意

  1. Exceptions 必须被声明
public Scanner(File sources) throws FileNotFoundException;
  1. Exceptions必须被处理
import java.util.Scanner;import java.io.File;import java.io.FileNotFoundException;public class ScannerFromFileWithCatch{    public static void main(String[] args){        try{            Scanner in = new Scanner(new File("test.in"));        }catch(FileNotFoundException ex){            ex.printStackTrace();        }    }}

7.2 方法调用堆栈 Method Call Stack

 一个典型的应用程序涉及许多级别的方法调用,这些调用由所谓的方法调用堆栈管理。堆栈是后进先出队列。 
public class test1{    public static void main(String []args){    System.out.println("Enter main()");    methodA();    System.out.println("Exit main()");    }    public static void methodA(){        System.out.println("Enter methodA()");        methodB();        System.out.println("Exit methodA()");    }    public static void methodB(){        System.out.println("Enter methodB()");        methodC();        System.out.println("Exit methodB()");    }    public static void methodC(){        System.out.println("Enter methodC()");        System.out.println("Exit methodC()");    }}
  • Output

Enter main()
Enter methodA()
Enter methodB()
Enter methodC()
Exit methodC()
Exit methodB()
Exit methodA()
Exit main()

image-20210612174508584

public class test1{    public static void main(String []args){        System.out.println("Enter main()");        methodA();        System.out.println("Exit main()");    }    public static void methodA(){        System.out.println("Enter methodA()");        methodB();        System.out.println("Exit methodA()");    }    public static void methodB(){        System.out.println("Enter methodB()");        methodC();        System.out.println("Exit methodB()");    }    public static void methodC(){        System.out.println("Enter methodC()");        System.out.println(1/0);        System.out.println("Exit methodC()");    }}
  • Output

Enter main()
Enter methodA()
Enter methodB()
Enter methodC()
Exception in thread “main” java.lang.ArithmeticException: / by zero
at MethodCallStackDemo.methodC(MethodCallStackDemo.java:XX)
at MethodCallStackDemo.methodB(MethodCallStackDemo.java:XX)
at MethodCallStackDemo.methodA(MethodCallStackDemo.java:XX)
at MethodCallStackDemo.main(MethodCallStackDemo.java:XX)

7.3 异常调用堆栈 Exception & Call Stack

  • Exception对象包含异常的类型,以及发生异常时程序的状态。

  • JVM负责查找异常处理程序来处理异常对象。它在调用堆栈中向后搜索,直到找到该异常对象的特定类的匹配异常处理程序。如果JVM在调用堆栈中的所有方法中都找不到匹配的异常处理程序,它将终止程序。

image-20210612175835906

7.4 Exception Classes

Throwable, Error, Exception & RuntimeException

image-20210612180124830

Throwable有两个子类

java.lang.Exception异常

描述由程序引起的错误(例如FileNotFoundException、IOException)

这些错误可以由您的程序捕获和处理

java.lang.Error错误

描述很少发生的内部系统错误。如果发生这样的错误,您几乎无能为力,程序将被Java运行时终止。

7.4.1 检查与未检查的异常

image-20210612180349045

Error和RuntimeException的子类称为unchecked异常。编译器不会检查这些异常,因此不需要捕获或声明在程序中抛出这些异常。

所有其他异常都称为检查异常。它们由编译器检查,必须被捕获或声明为抛出。

7.5 异常处理操作

Five keywords are used in exception handling: try, catch, finally, throws and throw.

Java’s exception handling consists of three operations:

  1. Declaring exceptions;

  2. Throwing an exception;

  3. Catching an exception.

public class TryCatchFinally{public static void main(String[] args){	try{        System.out.println("Start of the main logic");        System.out.println("Try opening a file");        Scanner in = new Scanner(new File("test.in"));        System.out.println("File Found,processing the file...");        System.out.println("End of the main logic");    }catch(FileNotFoundException ex){        System.out.println("finally-block runs regardless of the state of exception")    }    System.out.println("After try-catch-finally, life goes on");  }}

7.6 常见异常类common exception

ArrayIndexOutOfBoundsException

int[] anArray=new int[3];System.out.println(anArray[3]);

NullPointerException

String[] strs=new String[3];System.out.println(strs[0].length());

NumberFormatException

Integer.parseInt("abc");

ClassCastException

Object o=new Object();Integer i=(Integer)o;

IllegalArgumentException
IllegalStateException
NoClassDefFoundError

public class MyMagicExceptionTest{	public static void magic(int number) throws MyMagicException{	if(number==8){	throw (new MyMagicException("you hit the magic number"));	System.out.println("hello");	}  public static void main(String[] args){      try{          magic(9);          magic(8);      }catch(MyMagicException ex){          ex.printlnStackTrace();      }        }	}}

8. I/O

class_12_IO.pptx

8.1. 基本输入输出

编程简单的I/O操作很容易,只涉及几个类和方法。

编程效率高,可移植的I/O是非常困难的,特别是如果你必须处理不同的字符集。

JDK有两组I/O包:

标准I/O(在包java.io中)、用于基于流的I/O的JDK1.0,以及

新的I/O(在java.nio包中)jdk1.4用于更高效的基于缓冲区的I/O。

JDK1.5通过新类java.util.Scanner和Formatter引入格式化文本I/O,并使用类似C的printf()和format()方法使用格式说明符进行格式化输出。

JDK1.7通过新包java.NIO.file及其辅助包中所谓的NIO.2(非阻塞I/O)增强了对文件I/O的支持。它还引入了一种新的try with resources语法来简化close()方法的编码。

  • 命令行参数
public class TestArgs{    public static void main(String[] args){  		for(int i=0;i>args.length;i++){            System.out.println("args["+i"] is '"+args[i]+"'");        }    }}

8.2 Console I/o

The variable System.out enables you to write to standard output.
System.out is an object of type PrintStream.
The variable System.in enables you to read from standard input.
System.in is an object of type InputStream.
The variable System.err enables you to write to standard error.
System.err is an object of type PrintStream.

8.3 File

Files and File I/O

java.io包允许您执行以下操作:

创建文件对象

操纵文件对象

读取和写入文件流

文件类

File类旨在提供一个抽象,以独立于机器的方式处理文件和路径名的大多数依赖于机器的复杂性。文件名是一个字符串。File类是文件名及其目录路径的包装类。

创建一个新的文件类

File myFile;myFile=new File("myfile.txt");myFile=new File("MyDocs","myfile.txt");//可以创建一个文件类然后用它去定义其他文件File myDir=new File("MyDocs");myFile=new File(myDir,"myfile.txt");

image-20210613152506667

image-20210613152518088

public class test1{    public static void main(String []args){        File dir=new File("D:\\A大二下\\java\\test");        listRecursive(dir);    }    public static void listRecursive(File dir){        if(dir.isDirectory()){            File[] items=dir.listFiles();            for(File item:items){                System.out.println(item.getAbsoluteFile());                if(item.isDirectory())listRecursive(item);            }        }    }}
  • Output

D:\A大二下\java\test\sub
D:\A大二下\java\test\sub\xx.txt
D:\A大二下\java\test\test.txt
D:\A大二下\java\test\text.txt

8.3.1 File I/O

File对象封装了文件或路径的属性,但不包含从文件中读/写数据的方法。

为了执行I/O,您需要使用适当的javai/O类创建对象。对象包含从文件中读取/写入数据的方法。

Scanner和PrintWriter类还可用于从文本文件读/写字符串和数值

Writing Data Using PrintWriter

image-20210613153215634

Reading Data Using Scanner

image-20210613153308502

流I/O操作包括三个步骤:

  1. Open 通过构造适当的I/O流实例,与物理设备(例如,文件、网络、控制台/键盘)相关联的输入/输出流。
  2. Read 从打开的输入流到遇到“流结束”,或 写 到打开的输出流(可选地刷新缓冲输出)。
  3. Close 输入/输出流。

PrintWriter/Scanner

File对象封装了文件或路径的属性,但不包含从文件中读/写数据的方法。为了执行I/O,我们需要使用适当的java I/O类创建对象。

public class test1{    public static void main(String []args) throws FileNotFoundException {        Scanner input= new Scanner(new File("D:\\A大二下\\java\\test\\text.txt"));        System.out.println(input.nextLine());//        PrintWriter output = new PrintWriter("D:\\A大二下\\java\\test\\text.txt");        output.println("Java 101");        output.close();    }}

image-20210613153655554

  • Output

image-20210613154403581

buiabasb

image-20210613154310899

8.4 基于字节的I/O和字节流

image-20210613155043717

FileInputStream/FileOutputStream将二进制输入/输出流与外部文件相关联。FileInputStream/fileoutputstream中的所有方法都是从其超类继承的。

image-20210613155110614

image-20210613155115572

8.4.1 文件输入流

要构造FileInputStream,请使用以下构造函数:

public FileInputStream(String filename)
public FileInputStream(File file)

如果尝试使用不存在的文件创建FileInputStream,则会发生java.io.FileNotFoundException。

8.4.2 文件输出流

要构造FileOutputStream,请使用以下构造函数:
public FileOutputStream(String filename)
public FileOutputStream(File file)
public FileOutputStream(String filename, boolean append)
public FileOutputStream(File file, boolean append)
如果文件不存在,将创建一个新文件。
如果文件已经存在,前两个构造函数将删除文件中的当前内容。
保留当前内容并将新数据追加到文件中,请通过向append参数传递true来使用最后两个构造函数。

8.4.3 FilterInputStream FilterOnputStream

image-20210613155524735

过滤流是出于某种目的过滤字节的流。

基本字节输入流提供了一种只能用于读取字节的读取方法。如果要读取整数、双精度数或字符串,则需要一个过滤器类来包装字节输入流。

使用过滤器类可以读取整数、双精度数和字符串,而不是字节和字符。

FilterInputStream和FilterOutputStream是用于过滤数据的基类。当您需要处理原始数字类型时,请使用DataInputStream和DataOutputStream来过滤字节。

8.4.4 DataInputStream/DataOutputStream

image-20210613160401542

8.4.5 Buffered I/O Byte-Streams

为了缓冲、过滤或数据格式转换(在原始字节和基元类型之间),I/O流通常与其他I/O流分层或链接。

image-20210613160509726

public class test1{    public static void main(String []args) throws FileNotFoundException {       String inFileStr="test-in.jpg";       String outFileStr="test-out.jpg";       FileInputStream in=null;       FileOutputStream out=null;       long startTime,elapsedTime;       File fileIn=new File(inFileStr);       System.out.println("File size is "+fileIn.length()+"bytes");       try{           in=new FileInputStream(inFileStr);           out=new FileOutputStream(outFileStr);           startTime=System.nanoTime();           int byteRead;           while((byteRead=in.read())!=-1){               out.write(byteRead);           }           elapsedTime=System.nanoTime()-startTime;           System.out.println("Elapsed Time is "+(elapsedTime/100000.0)+" msec");       } catch (IOException ex) {           ex.printStackTrace();       }finally {           try {               {                   if (in!=null)in.close();                   if(out!=null)out.close();               }           }catch (IOException ex){               ex.printStackTrace();;           }       }    }}

8.4.6 ObjectInputStream & ObjectOutputStream

DataInputStream/DataOutputStream enables you to perform I/O for primitive type values and strings.
ObjectInputStream/ObjectOutputStream enables you to perform I/O for objects in addition for primitive type values and strings.

image-20210613163515755

可序列化接口

并非所有对象都可以写入输出流。可以写入对象流的对象称为可序列化的。

可序列化对象是java.io.serializable接口的实例。因此可序列化对象的类必须实现可序列化。

可序列化接口是一个标记接口。它没有方法,所以不需要在实现可序列化的类中添加其他代码。

通过实现这个接口,Java序列化机制可以自动化存储对象和数组的过程。

class MySerializedObject implements Serializable{    public int number;    public MySerializedObJect(int number){        this.number=number;    }    public int getNumber(){        return number;    } }

image-20210613163959545

image-20210613164003512

8.4.7 The transient Keyword

如果一个对象是可序列化的实例,但它包含不可序列化的实例数据字段,那么该对象可以序列化吗?

答案是否定的。

要使对象能够被序列化,可以使用transient关键字标记这些数据字段,以告诉JVM在将对象写入对象流时忽略这些字段。

Consider the following class: public class Foo implements java.io.Serializable {    private int v1;  private static double v2;  private transient A v3 = new A();  }class A { } // A is not serializable

When an object of the Foo class is serialized, only variable v1 is serialized. Variable v2 is not serialized because it is a static variable, and variable v3 is not serialized because it is marked transient.
If v3 were not marked transient, a java.io.NotSerializableException would occur.

9.GUI

class_13_GUI.pptx

9.1. 例子

image-20210613164319279

import javafx.application.Application;import javafx.event.EventHandler;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.layout.StackPane;import javafx.stage.Stage;public class test1 extends Application{    public Button btnHello;//Declare a “Button” control    @Override    public void start(Stage primaryStage){        btnHello =new Button();        btnHello.setText("Say Hello");        btnHello.setOnAction((evt->System.out.println("Hello World")));        //Construct a  scene graph of nodes        StackPane root=new StackPane();//The root of scene graph is a layout node        root.getChildren().add(btnHello);//The root node adds Button as a child        Scene scene=new Scene(root,300,100);//Construct a scene given the root of scene graph        primaryStage.setScene(scene);//The stage sets scene        primaryStage.setTitle("Hello");//Set Window's title        primaryStage.show();//Set Visible (show it)    }    public static void main(String []args){       launch(args);    }}

9.2 程序与事件驱动编程

  • Procedural programming is executed in procedural order.
    In event-driven programming, code is executed upon activation of events.
  • A Taste of Event-Driven Programming
    HandleEvent demo
  • Handling GUI Events
    Source object (e.g., button)
    Listener object contains a method for processing the event.

image-20210613191003189

9.3 Trace execution

public class HandleEvent extends Application {  public void start(Stage primaryStage) {OKHandlerClass handler1 = new OKHandlerClass();    btOK.setOnAction(handler1);    CancelHandlerClass handler2 = new CancelHandlerClass();    btCancel.setOnAction(handler2);    …        primaryStage.show(); // Display the stage  }}  class OKHandlerClass implements EventHandler<ActionEvent> {  @Override  public void handle(ActionEvent e) {    System.out.println("OK button clicked");   }} 

image-20210613191157958

9.4 事件类

image-20210613191336936

事件对象包含与事件相关的所有属性。

可以使用EventObject类中的getSource()实例方法标识事件的源对象。

EventObject的子类处理特殊类型的事件,例如按钮操作、窗口事件、鼠标移动和按键。

下一张幻灯片中的表列出了外部用户操作、源对象和生成的事件类型。

image-20210613191710562

9.4 delegation model

image-20210613192221650

Button btOK=new Buttion("OK");OKHandlerClass handler =new OKHandlerClass();btOK.setOnAction(handler);

10 Event

10.1. Inner class

监听器类是专门为GUI组件(例如按钮)创建监听器对象而设计的。它不会被其他应用程序共享。

因此,将frame类中的listener类定义为内部类是合适的。

内部类:一个类是另一个类的成员。

优点:在某些应用程序中,可以使用内部类使程序变得简单。

public class Test{    //Inner class    public class A{        ...    }}

内部类可以使程序简单明了。

内部类支持其包含的外部类的工作,并被编译成名为OuterClassName$InnerClassName.class的类。

例如,OuterClass中的内部类InnerClass被编译为OuterClass$InnerClass.class。

内部类可以声明为public、protected或private,并遵循应用于该类成员的相同可见性规则。

内部类可以声明为静态的。可以使用外部类名访问静态内部类。静态内部类不能访问外部类的非静态成员

10.2 匿名内部类

匿名内部类必须始终扩展超类或实现接口,但不能有显式的extends或implements子句。
匿名内部类被编译成名为OuterClassNamen.class的类。
例如,如果外部类测试有两个匿名内部类,则这两个类将编译为Test​1.class和Test2.class。

image-20210613193154030

10.3 使用Lambda表达式简化事件处理

Lambda表达式是java8中的一个新特性。Lambda表达式可以看作是一个语法简洁的匿名方法。
例如,使用(b)中的lambda表达式,可以将(a)中的以下代码简化为三行。

//abtEnlarge.setOnAction{    new EventHandler<ActionEvent>(){        @Override        public void handle(ActionEvent e){                    }    }});
//bbtEnlarge.setOnAction(e->{    });

10.3.1 Lambda表达式的基本语法

ambda表达式的基本语法是

$(type1\ param1,type2\ param2,…)->表达式$

$(type1\ param1,type2\ param2,…)->{语句;}$

  • 编译器可以显式声明或隐式推断参数的数据类型。

  • 如果只有一个参数没有显式数据类型,则可以省略括号。

lambda表达式中的语句都用于该方法。
如果它包含多个方法,编译器将无法编译lambda表达式。
因此,编译器要理解lambda表达式,接口必须正好包含一个抽象方法。这样的接口称为函数接口或单一抽象方法(single abstract method: SAM)接口。

10.4 The MouseEvent Class

image-20210613195107741

10.5 The KeyEvent Class

image-20210613195444394

10.6 The KeyCode Constants

image-20210613195603287

10.7 lambda 练习

interface LambdaFunction{  void call();}class FirstLambe{    public static void main(String[] args){        LambdaFunction lambdaFunction = ()->System.out.println("Hello world");        lambdaFunction.call();    }}
  • Output

Hello world

class test1{    interface LambdaFunction{        String intKind(int a);    }    public static void main(String[] args){        LambdaFunction lambdaFunction = (int i)-> {          if((i%2)==0)return "even";          else return "odd";        };        System.out.println(lambdaFunction.intKind(10));    }}
  • Output

even

class test1{    interface LambdaFunction{        int apply(int j);        boolean equals(java.lang.Object arg0);    }    public static void main(String[] args){        LambdaFunction lambdaFunction = i ->i*i;        System.out.println(lambdaFunction.apply(10));    }}
  • Output

100

11. thread

class_15_thread.pptx

Java支持单线程和多线程操作。单线程程序有一个入口点(main()方法)和一个出口点。
多线程程序有一个初始入口点(main()方法),后跟许多入口点和出口点,这些入口点和出口点与main()同时运行。
线程,也称为轻量级进程,是编程操作的单一顺序流,有明确的开始和结束。在线程的生命周期中,只有一个执行点。

image-20210613201806739

​ 多任务系统可以通过共享计算资源(如CPU、内存和I/O通道)来同时执行许多任务。

通常有两种多任务操作系统:

 协作多任务系统:每个任务必须自动地将控制权让给其他任务。 先发制人的多任务系统:任务被赋予CPU的时间片,一旦分配完毕,任务将被迫将控制权让给其他任务。 

​ 与重量级进程不同,线程是轻量级的,在单个进程内运行——它们共享相同的地址空间、分配的资源和该进程的环境。

​ 线程必须在运行的进程中划分出自己的资源。例如,线程有自己的堆栈、寄存器和程序计数器。在线程中运行的代码只在该上下文中工作,因此,线程(操作的顺序流)也称为执行上下文。

​ 多线程处理对于提供与用户更好的交互也是必要的。例如,在字处理器中,当一个线程正在打印或保存文件时,可以使用另一个线程继续键入。在GUI应用程序中,多线程是提供响应用户界面的关键。

​ 典型的Java程序在单个进程中运行,对多个进程不感兴趣。但是,在进程中,它经常使用多个线程来同时运行多个任务。

11.1 并发编程是困难的

编写多线程程序可能很棘手。

  • 实验:
    打开三本书的第一页,试着同时阅读这些书。
    读第一本书的几个单词,然后读第二本书的几个单词,然后读第三本书的几个单词,然后循环读第一本书的下几个单词,依此类推。

  • 多线程的挑战
    在书与书之间切换,简单地阅读,
    记住你在每本书中的位置,
    把你正在读的书移近一点,这样你就能看到它,把你没有读的书推到一边
    努力理解书的内容!

11.2 线程状态和生命周期

在任何时候,线程都处于几种线程状态之一。

image-20210613202450550

新的可运行状态
新线程在新状态下开始其生命周期。它将保持此状态,直到程序启动线程,从而使其处于可运行状态。处于可运行状态的线程被认为正在执行其任务。
等待状态
有时,可运行线程在等待另一个线程执行任务时会转换为等待状态。
定时等待状态
可运行线程可以在指定的时间间隔内进入定时等待状态。当时间间隔到期或等待的事件发生时,它将转换回可运行状态。
阻塞状态
当可运行线程尝试执行无法立即完成的任务时,它将转换为阻塞状态,并且必须临时等待该任务完成。
终止状态
可运行线程在成功完成任务或以其他方式终止(可能是由于错误)时进入终止状态(有时称为死状态)。

操作系统级别

操作系统对Java虚拟机(JVM)隐藏这些状态,JVM只看到可运行状态。
就绪运行状态之间的转换仅由操作系统处理。
在大多数操作系统中,每个线程都有少量的处理器时间,称为执行其任务的时间片 (timeslice)。

11.3 Thread Runnable

Thread类具有run()、start()和sleep()等方法,这些方法对多线程非常有用。
如果不重写run()方法,则将调用Thread类中的默认run()方法,而该方法不执行任何操作。
您可以通过对thread类的对象调用start()方法来创建线程

11.3.1 Thread

class test1 extends Thread{    public void  run(){        try{            sleep(1000);        }catch (InterruptedException ex){            ex.printStackTrace();        }        System.out.println("In run(); thread name is:"+getName());    }    public static void main(String[] args) {        Thread myThread = new test1();        myThread.start();        System.out.println("In main();thread name: ");        Thread.currentThread().getName();    }}
  • Output

In main();thread name:
In run(); thread name is:Thread-0

11.3.2 Runnable

class test1 implements Runnable {    public void run(){        System.out.println("In run(); thread namse is: ");        Thread.currentThread().getName();    }    public static void main(String[] args)throws Exception {        Thread myThread = new Thread(new test1()); //一定要upcasting,才能调用start()函数        myThread.start();        System.out.println("In main(); thread name is: "+Thread.currentThread().getName());    }}
  • Output

In main(); thread name is: main
In run(); thread namse is:

11.4 线程同步

​ 当多个线程共享一个对象并被其中一个或多个线程修改时,可能会出现不确定的结果(我们将在示例中看到),除非对共享对象的访问得到正确管理。
​ 这个问题可以通过一次只给一个线程对访问共享对象的代码的独占访问来解决。在此期间,希望访问该对象的其他线程一直在等待。当具有独占访问权限的线程完成对对象的访问时,允许其中一个等待线程继续。
​ 这个过程称为线程同步,协调多个并发线程对共享数据的访问。

11.4.1 Race Conditions

​ 线程共享内存,可以同时修改数据。由于修改可以在没有安全措施的情况下同时进行,这可能会导致不直观的结果。

image-20210613211741244 在synchronized块中,将synchronized关键字用于引用变量,并在其后跟随一个代码块。

package com.company;import java.lang.Thread.UncaughtExceptionHandler;public class TestThreeThreads{    public static void main(String[] args) {        PrintMe th1=new PrintMe("A");        PrintMe th2=new PrintMe("B");        PrintMe th3=new PrintMe("C");        th1.setName("A");        th2.setName("B");        th3.setName("C");        new Thread(th1).start();        new Thread(th2).start();        new Thread(th3).start();        while(true);    }}class PrintMe implements Runnable {    private String name;    PrintMe(String n){        this.name=n;    }    public void setName(String name) {        this.name = name;    }    @Override    public void run() {        for (int i = 0; i < 10; i++) {            System.out.println(this.name);        }        try {            System.out.println("停顿了 " + 2 + "秒");            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

11.4.2 sychronized

  • 必须持有监视器锁才能指定线程执行代码块,应该将代码放在synchronized语句中。
  • 这样的代码据说是由监视器锁保护的。
  • 使用synchronized关键字声明synchronized语句:
synchronized(object){     statements}

image-20210613212533977

image-20210613212538070

为了防止多线程对临界资源的访问有时会导致数据的不一致性,Java提供了“互斥”机制,可以为这些资源对象加上一把“互斥锁”,在任一时刻只能由一个线程访问,即使该线程出现阻塞,该对象的被锁定状态也不会解除,其他线程仍不能访问该对象,这就是多线程同步。线程同步是保证线程安全的重要手段,但是线程同步客观上会导致性能下降。

可以使用synchronized关键字通过两种方式实现线程同步:一种是synchronized方法,使用synchronized关键字修饰方法,对方法进行同步;另一种是synchronized语句,将synchronized关键字放在对象前面限制一段代码的执行。

11.4.2.1 多线程方法

package duoxiancheng;//机票数据库public class TicketDB {	//机票的数量	private int ticketCount=5;		//获得当前机票数量	public synchronized int getTicketCount()	{		return ticketCount;	}		//销售机票	public synchronized void sellTicket()	{		try {			//线程休眠,阻塞当前线程,模拟等待用户付款			Thread.sleep(1000);		} catch (InterruptedException e) {			e.printStackTrace();		}		System.out.printf("第%d号票,已经售出\n",ticketCount);		ticketCount--;	}}//调用代码如下package duoxiancheng; public class HelloWorld {	public static void main(String[] args) {		TicketDB db=new TicketDB();				//创建线程t1		Thread t1=new Thread(()->{			while(true)			{				int currTicketCount=db.getTicketCount();				//查询是否有票				if(currTicketCount>0)				{					db.sellTicket();				}				else				{					//无票退出					break;				}			}		});		//开始线程t1		t1.start();				//创建线程t2		Thread t2=new Thread(()->{			while(true)			{				int currTicketCount=db.getTicketCount();				//查询是否有票				if(currTicketCount>0)				{					db.sellTicket();				}				else				{					//无票退出					break;				}			}		});		//开始线程t2		t2.start();	}}

11.4.2.2 多线程语句

package duoxiancheng;public class HelloWorld {	public static void main(String[] args) {		TicketDB db=new TicketDB();				//创建线程t1		Thread t1=new Thread(()->{			while(true)			{				synchronized (db) {					int currTicketCount=db.getTicketCount();					//查询是否有票					if(currTicketCount>0)					{						db.sellTicket();					}					else					{						//无票退出						break;					}				}			}		});		//开始线程t1		t1.start();				//创建线程t2		Thread t2=new Thread(()->{			while(true)			{				synchronized (db) {					int currTicketCount=db.getTicketCount();					//查询是否有票					if(currTicketCount>0)					{						db.sellTicket();					}					else					{						//无票退出						break;					}				}			}		});		//开始线程t2		t2.start();	}}
public class MyThread implements Runnable{	private int ticket = 10; 	@Override	public void run() {		while(this.sale()) {}	}	public synchronized boolean sale() {			if(ticket<0) {				System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+ticket);				return false;			}			try {				Thread.sleep(10); // 延迟0.01秒,使得ticket可以被其它线程充分改变(可能此时的ticket小于等于0了)			}catch(InterruptedException e) {				e.printStackTrace();			}			System.out.println(Thread.currentThread().getName()  + " 正在售票,还剩余票数为:" + ticket--);			return true;		}}//public class ThreadDemo {	public static void main(String[] args) {		// 一份资源		Runnable mt1 = new MyThread();				// 共享同一份资源		new Thread(mt1,"售票员A").start();		new Thread(mt1,"售票员B").start();		new Thread(mt1,"售票员C").start();	}}

11.5 死锁

所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,死锁的操作一般是在程序运行时候才有可能出现,死锁是在多线程开发中较为常见的一种问题,过多的同步就有可能出现死锁。

class firstCorssBridge{	public synchronized void tell(secondCorssBridge scb) {		System.out.println("张三告诉王五:我先过,你后过,否则你别想过这桥!");		scb.cross();	}	// 以下函数不会执行	public synchronized void cross() {		System.out.println("张三快快乐乐地过桥了……");	}}class secondCorssBridge{	public synchronized void tell(firstCorssBridge fcb) {		System.out.println("王五告诉张三:我先过,你后过,否则你别想过这桥!");		fcb.cross();	}	// 以下函数不会执行	public synchronized void cross() {		System.out.println("王五快快乐乐地过桥了……");	}}public class DeadLock implements Runnable{	private firstCorssBridge fcb = new firstCorssBridge();	private secondCorssBridge scb = new secondCorssBridge();		public DeadLock() {		// 启动线程 并执行以下语句		new Thread(this).start(); // 会运行run函数		fcb.tell(scb); // 运行到里面时 fcb会等待scb	}	@Override	public void run() {		scb.tell(fcb); // 运行到里面时 scb会等待fcb	}		public static void main(String[] args) {		new DeadLock();	}}

Executor

要允许Runnable执行其任务,必须执行它。
Executor对象执行Runnables。它通过创建和管理一组称为线程池的线程来实现这一点。
当执行器开始执行Runnable时,执行器调用Runnable对象的run方法,该方法在新线程中执行。
Executor接口声明了一个名为execute的方法,该方法接受Runnable作为参数。

image-20210613215556091

JAVAFX Thread

JavaFX在处理多线程编程时有一系列独特的挑战。原因是JavaFX主要设计为以或多或少的线性方式工作。所有用户界面事件都在JavaFX应用程序线程中处理。
concurrent包管理与UI交互的多线程代码,并确保此交互在正确的线程中发生。JavaFX在处理多线程编程时有一系列独特的挑战。原因是JavaFX主要设计为以或多或少的线性方式工作。所有用户界面事件都在JavaFX应用程序线程中处理。
concurrent包管理与UI交互的多线程代码,并确保此交互在正确的线程中发生。

Worker接口提供后台Worker使用的API,后台Worker与UI通信。
因此,Worker是在后台线程中工作的对象。
Worker对象的状态可以从JavaFX应用程序的线程中观察和使用。
Task类是java.util.concurrent的完全可观察的实现。
FutureTask类,它允许开发人员在JavaFX应用程序中实现异步任务。

服务类执行这些任务。

package test;/* */import javafx.concurrent.Task;import  javafx.event.ActionEvent;import javafx.application.Application;import javafx.event.EventHandler;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Parent;import javafx.scene.Scene;import javafx.scene.control.*;import javafx.scene.layout.HBox;import javafx.scene.layout.Priority;import javafx.scene.layout.StackPane;import javafx.scene.layout.VBox;import javafx.stage.Stage;import java.io.InputStream;import java.lang.Thread;import java.lang.Runnable;import java.net.HttpURLConnection;import java.net.URL;import javafx.concurrent.Worker;class test1 extends Application {    private HBox bottomControls;    private ProgressBar pb;    private Label messageLabel;    private TextField tfURL;    private TextArea contents;    @Override    public void start(Stage primaryStage)throws Exception{        Parent p=createMaininView();        Scene scnene=new Scene(p);        primaryStage.setTitle("ProgressBarApp");        primaryStage.setWidth(667);        primaryStage.setHeight(376);        primaryStage.setScene(scnene);        primaryStage.show();    }    private Parent createMaininView(){        VBox vbox=new VBox();        vbox.setPadding(new Insets(10));        vbox.setSpacing(10);        HBox topControls=new HBox();        topControls.setAlignment(Pos.CENTER_LEFT);        topControls.setSpacing(4);        Label label=new Label("URL");        tfURL=new TextField();        HBox.setHgrow(tfURL, Priority.ALWAYS);        Button btnGetHTML=new Button("Get HTML");        btnGetHTML.setOnAction(this::getHTML);        topControls.getChildren().addAll(label,tfURL,btnGetHTML);        contents=new TextArea();        VBox.setVgrow(contents,Priority.ALWAYS);        bottomControls=new HBox();        bottomControls.setSpacing(4);        HBox.setMargin(bottomControls,new Insets(4));        pb=new ProgressBar();        messageLabel=new Label("");        bottomControls.getChildren().addAll(pb,messageLabel);        vbox.getChildren().addAll(topControls,contents,bottomControls);        return vbox;    }    public void getHTML(ActionEvent evt){        String url=tfURL.getText();        Task<String>task=new Task<String>() {            @Override            protected String call() throws Exception {                updateMessage("Getting HTML from"+url);                updateProgress(0.5d,1.0d);                HttpURLConnection c=null;                InputStream is=null;                String retval="";                try {                    c=(HttpURLConnection) new URL(url).openConnection();                    updateProgress(0.6d,1.0d);                    is=c.getInputStream();                    int ch;                    while((ch=is.read())!=-1){                        retval+=(char)ch;                    }                }finally {                    if(is!=null){                        is.close();                    }                    if(c!=null){                        c.disconnect();                    }                }                updateMessage("HTML retrived");                updateProgress(1.0d,1.0d);                return retval;            }            @Override            protected void succeeded(){                contents.setText(getValue());            }            @Override            protected void failed(){                Alert alert=new Alert(Alert.AlertType.getException.getMessage());                alert.showAndWait();            }        };        bottomControls.visibleProperty().bind(task.runningProperty());        pb.paddingProperty().bind(task.progressProperty());        messageLabel.textProperty().bind(task.messageProperty());        new Thread(task).start();    }    public static void main(String[] args)throws Exception {                launch(args);    }}

复习课

数组 (送)

控制流 (送)

x

1.类

继承: (树状结构)

多态:子类生成子类的对象,父类生成父类的对象

类型转换: down-casting(显式) ,up-casting (隐式) (不会考但是有细节)

显式不安全

Overloading

Overriding 父类和子类

2.集合 (必考)

Iterator

2.1 list

2.2 set

2.3map

reference class 的类型放入集合,要考虑去重

3.比较和排序

  • 重写接口
  • 写父类

comparator

comparable

4.异常

check的异常

unchecked的异常(错误+runtimeexception)

5.文件

文件的每种结构都要掌握

6. JAVAFX

7.线程和Runnable

加锁

是否会造成死锁

Network和数据库不考

1-10题送分,送分的跟去年一样

  1. ture true false true B
  2. 实例化一个抽象类 CZ
  1. C 看throw的类跟 catch 的类是否有继承关系

  2. B

  3. c

  4. b

  5. a

  6. a

  7. 都有

  8. B

  9. c

  10. 自己回去看 C

  11. b

多选题:


文章作者: Raina Jung
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Raina Jung !
  目录