zoukankan      html  css  js  c++  java
  • java基本语法

    1       下载安装

    1.1    java下载

    访问http://www.oracle.com/technetwork/java/javase/downloads/index.html 会出现如图1-1-1所示界面,

    图1-1-1

    然后把页面拉倒底部,如图1-1-2所示,

     

    图1-1-2

    点击DOWNLOAD,根据需要选择相应的版本下载(Java SE8),如图1-1-3,

    图1-1-3

    1.2    java安装

    安装步骤如下:

    图1-2-1

    图1-2-2

    图1-2-3

    图1-2-4

    接下来是配置环境变量鼠标右键点击计算机à属性,出现如图1-2-5所示,

    图1-2-5

    编辑Path追加;C:Program FilesJavajdk1.8.0_131in;C:Program FilesJavajre1.8.0_131in

    1.3验证安装是否成功

    Win+R(快捷键),输入cmd命令打开命令行窗口如图1-3-1,

     

    图1-3-1

    输入java命令如图1-3-2所示,说明安装成功

     

    图1-3-2

    1.3    Eclpse下载

    访问http://www.eclipse.org/downloads/ 如图2-1-1所示,

    图2-1-1

    点击Download Packages,如图2-1-2,根据自己电脑的实际情况选择32位或64位的下载

    下载之后,你会得到一个zip文件,将这个文件解压到你电脑中的任意一目录,然后打开这个目录,里面有一个eclipse.exe文件,双击这个文件,就可以启动你的eclipse了

    图2-1-3

    1.4    创建java 工程

    File ——》New——》Java Project

    Project name(输入工程名)——》点击Finish

    Src——》New——》Packget (创建包名)

    1.5    Maven下载安装

    1.5.1   下载

    http://maven.apache.org/download.cgi

    下载后解压到指定位置,我这里解压到D:Program Files

    1.5.2   安装Maven

    接下来就是设置环境变量。点开“我的电脑”->“属性”->“高级系统设置”->"高级”选项卡->“环境变量”窗口

    1.5.3   验证

    打开命令行,输入mvn --version

    1.5.4   配置 Maven

    在安装 Maven 的位置, apache-maven-3.3.9conf 下有一个 settings.xml 文件,这个就是 maven 的全局配置了。

    我们一般不修改全局配置,而是将 settings.xml 复制到自己项目的根目录下的 .m2 目录中 ( %Home%.m2 ),然后再来修改。

    打开 setting.xml ,我们一般修改两个地方:

    • 修改软件库: 找到 localRepository 部分, 用来指定软件的安装位置。这是个可选项,如果不指定,将使用默认位置

    <!-- localRepository

       | The path to the local repository maven will use to store artifacts.

       |

       | Default: ${user.home}/.m2/repository

      <localRepository>/path/to/local/repo</localRepository>

      -->

    • 修改下载服务器的镜像地址: 有时候我们不能连接到默认的下载服务器或速度很慢,可以通过修改这个部分来解决。 找到 mirrors 部分,然后修改就可以了。

    <mirror>

        <id>nexus-aliyun</id>

        <name>Nexus aliyun</name>

        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>

        <mirrorOf>central</mirrorOf>

    </mirror>

    1.5.5   配置 Eclipse

    在 Eclipse 中,点开 "Windows"->"Preferences",定位到 "Maven" 下的 "Installations", 将我们之前安装的 maven 位置添加进去并选中, 点击 "Apply"

    然后定位到 "Maven" 下的 "User Settings", 将之前我们修改的配置文件路径设置上去,点击 "apply"。

    1.5.6   使用 Maven 创建项目

    (我们以创建一个简单的 Web 项目为例来讲解)

    打开 Eclipse (使用较新的版本,本身就支持 Maven ), 在 Project Explorer 中右键 , New 一个 Other 项目

    然后 Next > , 选择项目位置, Next >,

    因为我们要建的是一个 Web 程序,所以选择 maven-archetype-webapp 。然后点击 Next >

    1.6    下载安装Android SDK

    Android SDK提供了开发Android应用程序所需的API库和构建、测试和调试Android应用程序所需的开发工具

    1.7    在线安装ADT

    在线安装ADT插件,菜单栏上选择 Help > Install New Software如图3-1-1所示,

    图3-1-1

    单击 Add 按钮,在右上角,在Name(名称)处输入"ADT Plugin",Location(网址)处输入https://dl-ssl.google.com/android/eclipse/点击OK,如图3-1-2,

     

    图3-1-2

    1.7.1   eclipse离线安装ADT插件

    访问:https://dl.google.com/android/ADT-23.0.6.zip

    #andriod开发者工具http://www.androiddevtools.cn/

    下载完成后,然后在菜单栏上选择 Help > Install New Software,如图3-1-1所示,单击 Add 按钮,在右上角,在"Add Repository"对话框,单击"Archive",选择下载的adt-23.0.6.zip文件并单击"确认",在Name(名称)处输入"ADT Plugin",单击“Finish” 如图3-2-1,

    图3-2-1

     

    在下一个窗口中,您会看到一个要下载的工具列表,单击“Next” 阅读并接受许可协议,然后单击“Finish”,安装完成后,重新启动Eclipse

    1.7.2   配置ADT插件

    启动Eclipse,选择windows>preferences>android

    图3-3-1

    Android SDK下载:https://dl.google.com/android/android-sdk_r24.0.2-windows.zip

    在选项卡中选择"Browse",选择之前下载的Android SDK的zip文件(需要解压)的目录(解压后的目录),点击确定,如图3-3-2,

    图3-3-2

     

     

     

     

     

     

    2       Java 源文件的命名规则

    l  通常情况下,Java 程序源文件的主文件名可以任意。

    l  但如果其中定义了一个 public 类,则该源文件的主文件名必须与该 public 类的类名相同。

    l  一个Java 源文件可包含多个类定义,但最多只能包含一个public类定义。

    2.1    为什么只能包含一个public类?

    因为 public 类可以被项目中其他包下的类访问到。只需要在使用前 import 其对应的 class 文件。将类名与文件名一一对应就可以方便虚拟机在相应的路径(包名)中找到相应的类的信息。如果不这么做的话,就很难去找,而且开销也会很大。

    1、Java编译器在编译的时候,如果整个Java文件(编译单元)都没有public类(对外的公开接口类),类加载器就无需直接去加载该编译单元产生的所有的字节码文件(.class文件),那么也就是无需去寻找编译后字节码文件存放位置。而类名和文件名一致是为了方便虚拟机在相应的路径中找到相应的类所对应的字节码文件。所以在没有public类的Java文件中,文件名和类名都没什么联系。

    2、如果编译单元中包含了public类,那么该类对应的字节码文件需要被类加载器加载的,这时候就需要让类加载器知道该字节码文件的位置,所以就要确保该类与Java文件名称一致。同时,如果有两个public类在同一个文件中,而一个文件只能有一个名称,故两个public类的名称就不能同时和文件名一样,这就造成至少其中有一个public类在编译的时候编译不通过。

    2.2    关于Java源文件命名,通常有以下建议

    l  一个 Java 源文件只定义一个类,不同的类使用不同的源文件定义。

    l  让 Java 源文件的主文件名与该源文件中定义的 pulblic 类同名

    2.3    主类

    由于java虚拟机需要调用类的main方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。

    类的名字和文件名一致,并且包含main函数的类,叫做主类。

    所谓主类是程序的入口,要让JVM能找到执行的入口,必须:

    • 类名字和文件名一致
    • 包含public static void main(String[] args) {}

    3       Java基本语法

    【Example】

    import java.util.Scanner;

    public class HelloWorld {  //【此HelloWorld与文件名一致(即HelloWorld.java)】

        public static void main(String []args) {

            System.out.println("Hello World");

                Scanner scan=new Scanner(System.in);

                    int num=scan.nextInt();  //【输入整型】

                    String line = scan.nextLine();  //【输入字符串】

                    System.out.println("num="+num);

    }}

    执行命令:

    $ javac HelloWorld.java

    $ java HelloWorld

    输入:32

    输出结果:

           

    3.1    基本数据类型

    Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。

    类型

    描述

    例子

    byte

    byte 数据类型是8位、有符号的, 默认值是 0

    byte a = 100, byte b = -50

    short

    short 数据类型是 16 位、有符号的, 默认值是 0

    short s = 1000, short r = -20000

    int

    数据类型是32位、有符号的, 默认值是 0

    int a = 100000, int b = -200000

    long

    数据类型是 64 位、有符号, 默认值是 0L

    long a = 100000L,Long b = -200000L

    float

    数据类型是单精度、32位, 默认值是 0.0f

    浮点数不能用来表示精确的值, 如货币

    float f1 = 234.5f

    double

    数据类型是双精度、64 位, 默认值是 0.0d,

    double类型同样不能表示精确的值, 如货币

    double d1 = 123.4

    boolean

    只有两个取值:true 和 false, 默认值是 false

    boolean one = true

    char

    char类型是一个单一的 16 位 Unicode 字符,默认值'u0000'

    char letter = 'A'

    String 类:

    String s = “Hello Java”;

    3.2    Java修饰符

    访问控制修饰符

    Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

    l  default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。

    l  private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

    l  public : 对所有类可见。使用对象:类、接口、变量、方法

    l  protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

    3.2.1   访问控制和继承

    请注意以下方法继承的规则:

    • 父类中声明为 public 的方法在子类中也必须为 public。
    • 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
    • 父类中声明为 private 的方法,不能够被继承。

    3.2.2   非访问修饰符

    为了实现一些其他的功能,Java 也提供了许多非访问修饰符。

    • static 修饰符,用来修饰类方法和类变量。
    • final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
    • abstract 修饰符,用来创建抽象类和抽象方法。
    • synchronized 和 volatile 修饰符,主要用于线程的编程

    3.2.3   static 修饰符

    1. static修饰的成员可以通过类名.方法(类名.变量)访问,即可以在不创建对象的情况下进行访问,当然也可以通过的对象的方式来访问;
    2. static修饰的成员成为类成员,为类的所有的实例所共享;
    3. static方法内部只能使用static修饰方法或成员变量;
    4. 因为static方法不需要实例就可以访问,因此static方法内容不能有this、super。
    5. 静态代码块:static修饰的代码块,当类被加载时执行,且只被执行一次。
    6. static修饰的方法可以被继承但不能重写
    • 静态变量

    static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性(变量)设置为类属性(类变量)。

    • 静态方法

    static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据;如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

    3.2.4   final 修饰符

    • final必须在声明是被初始化,或在非静态代码块,或在构造函数中初始化。
    • final 修饰的属性,是最终的属性,属性值不能被修改,即常量。
    • final修饰的方法,是最终的方法,该方法不能被修改,该方法不能被重写。
    • final修饰的类,是最终类,该类不能被扩展,该类不能被继承。
    • final 修饰符通常和 static 修饰符一起使用来创建类常量。

    3.2.5   abstract 修饰符

    抽象类:

    抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。

    一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。

    抽象类可以包含抽象方法和非抽象方法

    3.3    Java 运算符

    计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:

    3.3.1   算术运算符

    3.3.2   关系运算符

    3.3.3   位运算符

    3.3.4   逻辑运算符

    3.3.5   赋值运算符

    3.3.6   其他运算符-条件运算符(?:)

    3.4    循环结构

    3.4.1   while 循环

    public class Test {

       public static void main(String args[]) {

          int x = 10;

          while( x < 20 ) {

             System.out.print("value of x : " + x );

             x++;

             System.out.print(" ");

          }

       }

    }

    3.4.2   do…while 循环

    public class Test {

       public static void main(String args[]){

          int x = 10;

          do{

             System.out.print("value of x : " + x );

             x++;

             System.out.print(" ");

          }while( x < 20 );

       }

    }

    3.4.3   for 循环

    for(int x = 10; x < 20; x = x+1) {

        System.out.println("value of x : " + x );

    }

    3.5    条件语句

    3.5.1   if...else语句

    if 语句后面可以跟 else 语句,当 if 语句的布尔表达式值为 false 时,else 语句块会被执行

    if(布尔表达式){

    //如果布尔表达式的值为true

    }else{

    //如果布尔表达式的值为false

    }

    3.5.2   if...else if...else 语句

    if(布尔表达式 1){

    //如果布尔表达式 1的值为true执行代码

    }else if(布尔表达式 2){

    //如果布尔表达式 2的值为true执行代码

    }else if(布尔表达式 3){

        //如果布尔表达式 3的值为true执行代码

     }else {

    //如果以上布尔表达式都不为true执行代码

    }

    3.6    switch case 语句

    3.7    方法

    修饰符 返回值类型 方法名(参数类型 参数名){

        ...

        方法体

        ...

        return 返回值;

    }

    方法包含一个方法头和一个方法体。下面是一个方法的所有部分:

    修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。

    返回值类型 方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。

    方法名:是方法的实际名称。方法名和参数表共同构成方法签名。

    参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。

    方法体:方法体包含具体的语句,定义该方法的功能。

    3.7.1   可变参数

    • 定义方法时,在最后一个形参的类型后增加三点(… 位于变量类型和变量名之间,前后有无空格都可以),则表明该形参可以接受多个参数值,多个参数值被当成数字传入
    • 可变形参只能处于形参列表的最后,所以一个方法做多只能有一个长度可变的形参
    • 调用包含一个可变形参的方法时,可以为该形参传入0个或多个参数或一个数组
    • 能匹配定长的方法,那么优先匹配该方法,含有不定参数的重载方法被最后匹配
    • main方法的参数就是一个数组类型的,它可以改成可变参数类型
    • 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问

    【Example】

    public static void test(String ... args){

            System.out.println(args.length);

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

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

    }

    }

    public static main(String [] args){

            test("aa","bb","cc");

            test(new String [] { "MM","NN"});

    }

    3.8    类

    3.8.1   继承

    public class child extends father{}

    子类继承了父类,就继承了父类的方法和属性

    在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法

    子类不能继承父类中私有的(private)的成员变量和方法

    3.8.2   多态

    • 当父类类型的变量指向一个子类的对象,调用父类中已经被重写的方法的时候
    • 在多态的情况下(当父类类型的变量指向一个子类的时候):不能调用子类新添加的方法。
    • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再直接访问子类中添加的属性和方法,可以通过对对象进行强制的类型转换[Man man = (man)p;]

    class Person(){

    public void walk(){

                    System.out.println(“walk”);

    }

    }

    class Man extends Person(){

            @Override

    public void walk(){

                    System.out.println(“man walk”);

    }

    public void work(){

            System.out.println(“man work”);

    }

            public static void main(String[] args) {

                    Person p = new Man();

                    p.walk();

                    p.work(); //报错

                    Man man = (man)p; //强制转换

                    man.work();

            }

    }

    【Example】

    public class TestPerson{

                    public void test(Person p) {

                    if (p instanceof Person) {

                                   p.walk();

                           }

                    }

            public static void main(String[] args) {

                    TestPerson p = new TestPerson();

                    p.test(new Person()); //the person to walk

                    p.test(new Man()); //man is walking

                    p.test(new Woman()); //the woman walking

            }

    }

    3.8.2.1  instanceof操作符

    x instanceof A :检验x是否为A的对象,返回值为boolean型。

    要求x所属的类与类A必须是子类和父类的关系,否则编译错误。

    如果x属于类A的子类B,x instanceof A值也为true

    3.8.3   构造器(构造方法)

    作用:利用构造器参数初始化对象的属性

    语法格式:

    <修饰符> <类名> ([<参数表>]){

            【语句】

    }

    默认构造器:<类名>(){}

    注:

    Java类中,一旦定义了一个或多个构造方法。系统将不再提供默认的构造方法(默认调用无参数构造方法)。

    子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。

    构造器的名称必须与类名相同。修饰符:public、private、protected

    构造器不是方法,没有返回值(void也不能写)

    3.8.4   重写(Override)与重载(Overload)

    重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

    class Animal{

       public void move(){

          System.out.println("动物可以移动");

       }

    }

    class Dog extends Animal{

       public void move(){

          System.out.println("狗可以跑和走");

       }

    }

    public class TestDog{

       public static void main(String args[]){

          Animal a = new Animal(); // Animal 对象

          Animal b = new Dog(); // Dog 对象

          a.move();// 执行 Animal 类的方法

          b.move();//执行 Dog 类的方法

       }

    }

    方法的重写规则:

    方法名称参数列表返回值类型必须完全与被重写方法的相同。

    l  访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。

    l  父类的成员方法只能被它的子类重写。

    l  声明为 final 的方法不能被重写。

    l  声明为 static 的方法不能被重写,但是能够被再次声明。

    l  子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

    l  子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

    l  重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

    l  构造方法不能被重写。

    l  如果不能继承一个方法,则不能重写这个方法。

    重载(overloading) :是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同

    重载规则:

    l  被重载的方法必须改变参数列表(参数个数或类型不一样);

    l  被重载的方法可以改变返回类型;

    l  被重载的方法可以改变访问修饰符;

    l  被重载的方法可以声明新的或更广的检查异常;

    l  方法能够在同一个类中或者在一个子类中被重载。

    l  无法以返回值类型作为重载函数的区分标准

    总结

    方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。

    (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。

    (2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。

    (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现

    3.8.5   抽象类

    抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。

    抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。

    3.8.5.1  抽象方法

    Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。

    抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

    public abstract class Employee

    {

       private String name;

       public abstract double computePay();

    }

    声明抽象方法会造成以下两个结果:

    • 如果一个类包含抽象方法,那么该类必须是抽象类。
    • 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。

    3.8.5.2  抽象类总结规定

    1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

    2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

    4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。

    5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

    3.8.6   接口

    接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

    3.8.6.1  接口与类相似点

    • 一个接口可以有多个方法。
    • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
    • 接口的字节码文件保存在 .class 结尾的文件中。
    • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
    • 接口不能用于实例化对象。
    • 接口没有构造方法。
    • 接口中所有的方法必须是抽象方法。
    • 接口不能包含成员变量,除了 static 和 final 变量。
    • 接口不是被类继承了,而是要被类实现。
    • 接口支持多继承
    • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错),不必使用abstract关键字。
    • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误),不必使用abstract关键字。
    • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
    • 接口中的方法都是公有的。

    3.8.6.2  接口与类的区别

    3.8.6.3  接口特性

    3.8.6.4  抽象类和接口的区别

    1. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

    2. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    3.8.6.5  接口的声明

    [可见度(public)] interface 接口名称 [extends 其他的接口名] {

    //任何类型 final, static 字段

    // 抽象方法

    }

    interface Animal {

        public static final String name="Animal" ;

    public void eat();

    public void travel();

    }

    3.8.6.6  接口的实现

    • 当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
    • 类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。

    public class MammalInt implements Animal{

       public void eat(){

          System.out.println("Mammal eats");

       }

       public void travel(){

          System.out.println("Mammal travels");

       }

    public static void main(String args[]){

              MammalInt m = new MammalInt();

              m.eat(); // Mammal eats

              m.travel(); // Mammal travels

              System.out.println(MammalInt.name); // Animal

    }}

    3.8.6.7  接口的继承

    // 文件名: Sports.java public interface Sports { public void setHomeTeam(String name); public void setVisitingTeam(String name); }

    // 文件名: Football.java public interface Football extends Sports { public void homeTeamScored(int points); public void visitingTeamScored(int points); public void endOfQuarter(int quarter); }

    // 文件名: Hockey.java public interface Hockey extends Sports { public void homeGoalScored(); public void visitingGoalScored(); public void endOfPeriod(int period); public void overtimePeriod(int ot); }

    3.8.6.8  接口的多继承

    public interface Hockey extends Sports, Event

    3.8.6.9  标记接口

    最常用的继承接口是没有包含任何方法的接口。

    标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。

    标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。

    没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:

    建立一个公共的父接口:

    正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。

    向一个类添加数据类型:

    这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。

    3.8.7   内部类

    可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类

    3.8.7.1  成员内部类

    成员内部类是最普通的内部类,它的定义为位于另一个类的内部

    class Circle {

        private double radius = 0;

        public static int count =1;

        public Circle(double radius) {

            this.radius = radius;

        }

        private Draw getDrawInstance() {

            return new Draw();

        }

        class Draw {     //内部类

            public void drawSahpe() {

                System.out.println(Circle.this.radius);  //外部类的private成员

                System.out.println(count);   //外部类的静态成员

            }

        }

       

        public static void main(String[] args) {

            //方式一

            Circle cirlce = new Circle(12);

             Circle.Draw draw = cirlce.new Draw();

             //Circle.Draw draw = new Circle(12).new Draw();

            draw.drawSahpe();

            //方式二

            Circle.Draw  draw1 = cirlce.getDrawInstance();

            draw1.drawSahpe();

       

        }

    }

    • 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员
    • 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
    • 内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限
    • 外部类只能被 public 和包访问两种权限修饰

    3.8.7.2  局部内部类

    局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

    class People{
        public People() {
             
        }
    }
     
    class Man{
        public Man(){
             
        }
         
        public People getWoman(){
            class Woman extends People{   //局部内部类
                int age =0;
            }
            return new Woman();
        }
    }

    注意: 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。

    3.8.7.3  匿名内部类

    匿名内部类也就是没有名字的内部类,正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。

    3.8.7.3.1        匿名内部类的基本实现

    abstract class Person {

        public abstract void eat();

    }

    public class Demo {

        public static void main(String[] args) {

            Person p = new Person() {

                public void eat() {

                    System.out.println("eat something");

                }

            };

            p.eat();

        }

    }

    3.8.7.3.2        在接口上使用匿名内部类

    interface Person {

        public void eat();

    }

    public class Demo {

        public static void main(String[] args) {

            Person p = new Person() {

                public void eat() {

                    System.out.println("eat something");

                }

            };

            p.eat();

        }

    }

    运行结果:eat something

    3.8.7.4  静态内部类

    静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

    public class Test {
        public static void main(String[] args)  {
            Outter.Inner inner = new Outter.Inner(); // Construct Inner...
        }
    }
     
    class Outter {
        public Outter() {
             System.out.println("Construct Outter...");
        }
         
        static class Inner {
            public Inner() {
                 System.out.println("Construct Inner...");
            }
        }
    }

    3.8.8   this

    • 调用本类中的属性,也就是类中的成员变量;
    • 调用本类中的其他方法;
    • 调用本类中的其他构造方法,调用时要放在构造方法的首行。
    • this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现this

    3.8.9   super

    l  在子类的构造方法中可使用super(参数列表)语句调用父类的构造方法

    l  如果子类的构造方法中没有显示地调用父类构造方法,也没有使用this关键字调用重载的其它构造方法,则系统默认调用父类无参数的构造方法

    l  如果子类构造方法中既未显示调用父类构造方法,而父类中又没有无参的构造方法,则编译出错

    super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类

    super也有三种用法:

    1. 普通的直接引用
    2. 子类中的成员变量或方法与父类中的成员变量或方法同名
    3. 引用构造函数

    super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。

    3.8.10 ==操作符与equals方法的区别

    ==:引用类型比较引用(是否指向同一个对象)

    equals():对类File、String、Date及封装类(Wrapper Class)是比较类型及内容而不考虑引用是否是同一个对象,因为它们重写了equals方法。

    String s1 = new String("Tom");

    String s2= new String("Tom");

    System.out.println(s1.equals(s2)); // true

    System.out.println(s1==s2); // false

                   

    String s3 = "Hello";

    String s4 = "Hello";

    System.out.println(s3.equals(s4)); //true

    System.out.println(s3==s4); //true

    3.9    异常

    Java异常程序运行过程中所发生的异常事件可分为两类:

    Error:JVM系统内部错误,资源耗尽等严重情况

    Exception:其它因编译错误或偶然的外在因素导致的一般性问题

    常见异常:

    算术异常类:ArithmeticExecption

    空指针异常类:NullPointerException

    类型强制转换异常:ClassCastException

    数组负下标异常:NegativeArrayException

    数组下标越界异常:ArrayIndexOutOfBoundsException

    违背安全原则异常:SecturityException

    文件已结束异常:EOFException

    文件未找到异常:FileNotFoundException

    字符串转换为数字异常:NumberFormatException

    【Example】

    int a =90;

            int b = 0;

            try {

                    System.out.println(name[1]);

                    System.out.println(a/b);

            }catch (ArithmeticException e) {

                    System.out.println("es="+e);

            } catch (RuntimeException e) { //异常类的父类要放在异常类子类的后面

                           System.out.println(e);

            }finally {

                    System.out.println("finally ...");

            }

    结果:

    e=java.lang.ArithmeticException: / by zero

    finally ...

    3.9.1   抛出异常

    throw : 将产生的异常抛出(强调的是动作),抛出的既可以是异常的引用,也可以是异常对象。(位置: 方法体内)

    throws : 如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。用它修饰的方法向调用者表明该方法可能会抛出异常(可以是一种类型,也可以是多种类型,用逗号隔开)(位置: 写在方法名 或方法名列表之后 ,在方法体之前。)

    【throws】

    注:重写方法不能抛出比被重写方法范围更大的异常类型。

    public class TestThrows {

            //可以抛出多个异常,多个异常使用逗号(,)分割,多个异常没有顺序之分

            public static void test() throws Exception,ClassNotFoundException{

                    Class.forName("com.main.Person");

                    System.out.println("throws test()...");

            }

            public static void main(String[] args) throws ClassNotFoundException{

                    // TODO Auto-generated method stub

                    System.out.println("throws ...");

                    test();

            }}

    【throw】

    try{

        ArrayIndexOutOfBoundsException  exception = new ArrayIndexOutOfBoundsException();

    throw exception;//new ArrayIndexOutOfBoundsException();

    }catch(ArrayIndexOutOfBoundsException e){

         System.out.println("thorw抛出异常:"+e);

    }

    void doA(int a) throws IOException{

       try{

             ......

       }catch(Exception1 e){

          throw e;

       }catch(Exception2 e){

          System.out.println("出错了!");

       }

       if(a!=b)

          throw new Exception3("自定义异常");

    }

    3.9.2   自定义异常

    • 自定义异常,自己定义的异常类,通常情况下继承RuntimeException
    • 自定义异常的作用:看见异常类的名字,能快速知道出现了什么问题
    • 自定义异常通常都需要用throw关键字进行抛出

    public class exDef extends RuntimeException{

            private static final long serialVersionUID = 1L;

            public exDef(){}

            public exDef(String msg) {

                    super(msg);

            }}

    public class Test  {

            public static int ecm(int i,int j) {

                    if(i<0 || j<0) {

                           throw new exDef("不能输入负数");

                    }

                    return i/j;

            }

            public static void main(String[] args) {

                    System.out.println(ecm(2,-3));

            }}

    3.10 数组

    3.10.1 一维数组

    String [] names = new String[10];

    int [] scores = new int[5];

    int [] list1={1,2,3,4};

    for(int d:list1){

            System.out.println(d);

    }

    3.10.2 二维数组

    int [][] test = new int[5][4];

    int[][] tt = new int[2][];

    tt[0] = new int[2];

    tt[1] = new int[4];

    int[][] test = {{1,2},{4,5,6}};

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

            for (int j=0;j<test[i].length;j++) {

                    System.out.println("test["+i+"]["+j+"]="+test[i][j]);

            }

    }

    String[][] array_list = new String[3][];

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

                           array_list[i] = new String[i+1];

                           for(int j=0;j<array_list[i].length;j++) {

                                   array_list[i][j] =  i + "Hello"  + j;

                           }

                    }

                    for (String[] list:array_list) {

                           for(String name :list) {

                                   System.out.println(name);

                           }

                    }

    注:多维数组不一定是规则矩阵形式

    3.11 堆与栈

    3.11.1 栈

    • 函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
    • 栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
    • 栈内存中的数据,没有默认初始化值,需要手动设置。
    • 堆内存用来存放new创建的对象和数组。
    • 堆内存中所有的实体都有内存地址值。
    • 堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
    • 堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动清除,这也是JAVA优于C++的表现之一(C++中需要程序员手动清除)。

    3.11.2 堆

    3.12 Number & Math &Random类

    所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。

    3.12.1 Math

    静态导入:

    import static java.lang.Math.*;

    导入指定类的静态属性和静态方法;

    Integer x = 5;

    方法

    描述

    例子

    xxxValue() ;

    doubleValue()

    xxxValue() 方法用于将 Number 对象转换为 xxx 数据类型的值并返回

    x.byteValue() ;

    Integer.parseInt("101101011",2)

    将字符串转换当做2进制

    363

    compareTo()

    将number对象与参数比较

    x.compareTo(3)

    equals()

    判断number对象是否与参数相等

    x.equals(15)

    valueOf()

    返回一个 Number 对象指定的内置数据类型

    Integer.valueOf("20",16)//16进制;32

    Integer.valueOf("12")

    string转换成int

    String.valueOf(12)

    int转换成string

    toString()

    以字符串形式返回值

    x.toString()

    parseInt()

    将字符串解析为int类型

    Integer.parseInt("9")

    abs()

    返回参数的绝对值

    Math.abs(-12)

    ceil()

    返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型

    Math.ceil(12.23)  //13

    floor()

    返回小于等于(<=)给定参数的最大整数

    Math.floor(12.23)  //12

    rint()

    返回与参数最接近的整数。返回类型为double

    Math.rint(12.53)  //13.0

    round()

    四舍五入

    Math.round(12.53) //13

    min()

    返回两个参数中的最小值

    Math.min(12.123, 12.456)

    max()

    返回两个参数中的最大值

    exp()

    返回自然数底数e的参数次方

    log()

    返回参数的自然数底数的对数值

    pow()

    返回第一个参数的第二个参数次方

    sqrt()

    求参数的算术平方根

    sin()

    求指定double类型参数的正弦值

    cos()

    求指定double类型参数的余弦值

    tan()

    求指定double类型参数的正切值

    asin()

    求指定double类型参数的反正弦值

    acos()

    求指定double类型参数的反余弦值

    atan()

    求指定double类型参数的反正切值

    atan2()

    将笛卡尔坐标转换为极坐标,并返回极坐标的角度值

    toDegrees()

    将参数转化为角度

    Math.toDegrees(45.0)

    // 2578.3100780887044

    toRadians()

    将角度转换为弧度

    Math.toRadians(45.0)

    // 0.7853981633974483

    random()

    返回一个随机数

    Math.random()

    3.12.2 Random

    import java.util.Random;

    Random random = new Random();

    //random.setSeed(23); //设置种子

    System.out.println(random.nextFloat());

    System.out.println(random.nextInt(10)); //返回10以内的整数

                   

    3.13 Character 类

    Character 类用于对单个字符进行操作。

    Character 类在对象中包装一个基本类型 char 的值

                char ch = 'a';

                   // Unicode 字符表示形式

                    char uniChar = 'u039A';

                    // 字符数组

                    char[] charArray ={ 'a', 'b', 'c', 'd', 'e' };

                    Character cha = new Character('a');

                    System.out.println(ch); //a

                    System.out.println(uniChar); //K

                    System.out.println(cha); //a

    方法

    描述

    例子

    isLetter()

    是否是一个字母

    Character.isLetter('a')

    isDigit()

    是否是一个数字字符

    Character.isDigit('1')

    isWhitespace()

    是否是一个空白字符,空白符包含:空格、tab 键、换行符

    Character.isWhitespace(' ')

    isUpperCase()

    是否是大写字母

    Character.isUpperCase('c')

    isLowerCase()

    是否是小写字母

    Character. isLowerCase(‘c’)

    toUpperCase()

    指定字母的大写形式

    Character.toUpperCase('A')

    toLowerCase()

    指定字母的小写形式

    Character.toLowerCase('A')

    toString()

    返回字符的字符串形式,字符串的长度仅为1

    Character.toString('A')

    3.14 String类

    • String是不可变的字符序列
    • equals():比较字符串内容是否相等必须使用该方法,而不能直接使用==

    ==:引用类型比较引用(是否指向同一个对象),equals():对类File、String、Date及封装类(Wrapper Class)是比较类型及内容而不考虑引用是否是同一个对象,因为它们重写了equals方法。

    String greeting = "Hello,Java";

    String s = "HelloWorld";

    System.out.println(s); //HelloWorld

    char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};

    String helloString = new String(helloArray); 

    System.out.println( helloString ); //runoob

    创建格式化字符串

    System.out.printf("浮点型变量的值为 " + "%f, 整型变量的值为 " + " %d, 字符串变量的值为 " + "is %s", floatVar, intVar, stringVar);

    String s = "Hello Java";

    方法

    描述

    例子

    char charAt(int index)

    返回指定索引处的 char 值

    s.charAt(0) //H

    String concat(String str)

    将指定字符串连接到此字符串的结尾

    s.concat("!") //Hello Java!

    copyValueOf(char[] data)

    返回指定数组中表示该字符序列的 String

    copyValueOf(char[] data, int offset, int count)

    返回指定数组中表示该字符序列的 String

    startsWith(String prefix)

    试此字符串是否以指定的前缀开始

    s.startsWith("Hello")

    startsWith(String prefix, int toffset)

    测试此字符串从指定索引开始的子字符串是否以指定前缀开始

    s.startsWith("llo",2)

    endsWith(String suffix)

    测试此字符串是否以指定的后缀结束

    s.endsWith("Java")

    equals(Object anObject)

    将此字符串与指定的对象比较

    hashCode() 

    方法用于返回字符串的哈希码

    s.hashCode()

    length()

    用于返回字符串的长度

    s.length()

    trim()

    方法用于删除字符串的头尾空白符

    s.trim()

    toLowerCase()

    方法将字符串转换为小写

    s.toLowerCase()

    toUpperCase()

    方法将字符串小写字符转换为大写

    s. toUpperCase()

    substring(int beginIndex)

    返回字符串的子字符串

    s. substring(4)

    substring(int beginIndex, int endIndex)

    返回字符串的子字符串,起始索引(包括), 索引从 0 开始, 结束索引(不包括)

    s. substring(4,10)

    contains(CharSequence s)

    判断是否包含字符串返回布尔值

    s.contains("ab")

    String.format("hello %s %d", "Tom",1)

    hello Tom 1

    String.join("-", arrStr)

    String[] arrStr=new String[]{"a","b","c"};

    返回:a-b-c

    3.14.1 copyValueOf() 方法有两种形式:

    public static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的字符串。

    public static String copyValueOf(char[] data, int offset, int count): 返回指定数组中表示该字符序列的 字符串

    public class Test {

    public static void main(String args[]) {

    char[] Str1 = {'h', 'e', 'l', 'l', 'o', ' ', 'r', 'u', 'n', 'o', 'o', 'b'};

    String Str2 = "";

    Str2 = Str2.copyValueOf( Str1 );

            System.out.println("返回结果:" + Str2);

    Str2 = Str2.copyValueOf( Str1, 2, 6 );

    System.out.println("返回结果:" + Str2);

     } }

    3.14.2 indexOf() 方法

    indexOf() 方法有以下四种形式:

    • public int indexOf(int ch): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
    • public int indexOf(int ch, int fromIndex): 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
    • int indexOf(String str): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
    • int indexOf(String str, int fromIndex): 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

    3.14.3 matches() 方法

    matches() 方法用于检测字符串是否匹配给定的正则表达式。

    调用此方法的 str.matches(regex) 形式与以下表达式产生的结果完全相同:

    Pattern.matches(regex, str)

    public class Test {

        public static void main(String args[]) {

            String Str = new String("www.runoob.com");

     

            System.out.print("返回值 :" );

            System.out.println(Str.matches("(.*)runoob(.*)"));

            

            System.out.print("返回值 :" );

            System.out.println(Str.matches("(.*)google(.*)"));

     

            System.out.print("返回值 :" );

            System.out.println(Str.matches("www(.*)"));

        }

    }

    以上程序执行结果为:

    返回值 :true
    返回值 :false
    返回值 :true

    3.14.4 split() 方法

    split() 方法根据匹配给定的正则表达式来拆分字符串。

    注意: . 、 $、 | 和 * 等转义字符,必须得加 \。

    注意:多个分隔符,可以用 | 作为连字符

    public String[] split(String regex)

    public String[] split(String regex, int limit)

    参数:

    regex -- 正则表达式分隔符。

    limit -- 分割的份数

    String str = new String("Welcome-to-Runoob");

    for (String retval: str.split("-", 0)){  // str.split("-")

          System.out.println(retval); // Welcome   to    Runoob

     }

    3.14.5 replace() 方法

    replace() 方法通过用 newChar 字符替换字符串中出现的所有 oldChar 字符,并返回替换后的新字符串

    • public String replace(char oldChar,char newChar)

    System.out.println(Str.replace('o', 'T'));

     

    • public String replaceAll(String regex, String replacement)

    String Str = new String("www.google.com");

    System.out.println(Str.replaceAll("(.*)google(.*)", "runoob" )); // runoob

    • public String replaceFirst(String regex,String replacement)

    replaceFirst() 方法使用给定的参数 replacement 替换字符串第一个匹配给定的正则表达式的子字符串

    String Str = new String("hello runoob,I am from runoob");

    System.out.println(Str.replaceFirst("runoob", "google" )); // hello google,I am from runoob

    System.out.println(Str.replaceFirst("(.*)runoob(.*)", "google" )); // google

    3.15 StringBuffer 和 StringBuilder 类

    • 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
    • StringBuilder 类和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
    • 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类

    public class Test{

    public static void main(String args[]){

    StringBuffer sBuffer = new StringBuffer("Hello,");

    sBuffer.append("Java,")

    .append("JavaScript");

    System.out.println(sBuffer); // Hello,Java,JavaScript

    } }

    StringBuffer sBuffer = newStringBuffer("Hello")

    方法

    描述

    例子

    StringBuffer append(String s)

    将指定的字符串追加到此字符序列

    sBuffer.append("Java")

    StringBuffer reverse()

    将此字符序列用其反转形式取代

    sBuffer.reverse()

    delete(int start, int end)

    移除此序列的子字符串中的字符

    sBuffer.delete(0,3)

    insert(int offset, int i)

    将 int 参数的字符串表示形式插入此序列中

    replace(int start, int end, String str)

    使用给定 String 中的字符替换此序列的子字符串中的字符

    charAt(int index)

    返回此序列中指定索引处的 char 值

    indexOf(String str)

    返回第一次出现的指定子字符串在该字符串中的索引

    length()

    返回长度(字符数)

    substring(int start, int end)

    返回一个新的 String,它包含此序列当前所包含的字符子序列

    3.16 数组

    type[][] i = new type[2][3];

    int[][] a2=new int[][]{{1,2,3,4},{5,6,7,8,1}};

    String [] aves=new String[] {"huangshan","longyan","meiqing","huase"};

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

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

    }

    3.17 日期时间

    SimpleDateFormat来实现日期的格式化

    import java.util.Date;

    import java.text.SimpleDateFormat;

    Date d = new Date();

    long time = d.getTime();

    System.out.println(time);   //1578231241040

    System.out.println(d.toString());  //Sun Jan 05 21:34:01 CST 2020

    Date date = new Date(time);

    System.out.println(date);  //Sun Jan 05 21:36:19 CST 2020

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    String date_string = formatter.format(date);

    System.out.println(date_string);  //2020-01-05 21:42:20

    3.18 正则表达式

    import java.util.regex.Matcher;

    import java.util.regex.Pattern;

    public class RegexMatches

    {

        public static void main( String args[] ){

          // 按指定模式在字符串查找

          String line = "This order was placed for QT3000! OK?";

          String pattern = "(\D*)(\d+)(.*)";

          // 创建 Pattern 对象

          Pattern r = Pattern.compile(pattern);

          // 现在创建 matcher 对象

          Matcher m = r.matcher(line);

          if (m.find( )) {

             System.out.println("Found value: " + m.group(0) );

             System.out.println("Found value: " + m.group(1) );

             System.out.println("Found value: " + m.group(2) );

             System.out.println("Found value: " + m.group(3) );

          } else {

             System.out.println("NO MATCH");

          }

       }

    }

    3.19 Java 流(Stream)、文件(File)和IO

    3.19.1 BufferedInputStream、BufferedOutputStream

    //读取文件(缓存字节流)

    BufferedInputStream in = new BufferedInputStream(new FileInputStream("test.txt"));

    //写入相应的文件

    BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("test.txt"));

    //读取数据

    //一次性取多少字节

    byte[] bytes = new byte[2048];

    //接受读取的内容(n就代表的相关数据,只不过是数字的形式)

    int n = -1;

    //循环取出数据

    while ((n = in.read(bytes,0,bytes.length)) != -1) {

        //转换成字符串

        String str = new String(bytes,0,n,"GBK");

        System.out.println(str);

        //写入相关文件

        out.write(bytes, 0, n);

    }

    //清楚缓存

    out.flush();

    //关闭流

    in.close();

    out.close();

    3.19.2 BufferedWriter、BufferedReader

    try {

       //

       //BufferedWriter out = new BufferedWriter(new FileWriter("test.txt"));

        BufferedWriter out = new BufferedWriter(

            new OutputStreamWriter(new FileOutputStream("test.txt"),"UTF-8"));

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

            out.write(array_str[i]+" ");

        }

        out.flush();

        out.close();

       

        //

        //BufferedReader in = new BufferedReader(new FileReader("test.txt")));

        BufferedReader in = new BufferedReader(

                new InputStreamReader(new FileInputStream("test.txt"),"UTF-8"));

       

        String str = null;

        while((str = in.readLine()) != null) {

            System.out.println(str);

        }      

    }catch(IOException e) {

      System.out.println(e);

    }

    4       数据结构

    Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:

    4.1    枚举(Enumeration)

    import java.util.Vector;

    import java.util.Enumeration;

    public class EnumerationTester {

    public static void main(String args[]) {

          Enumeration<String> days;

          Vector<String> dayNames = new Vector<String>();

          dayNames.add("Sunday");

          dayNames.add("Monday");

          dayNames.add("Tuesday");

          dayNames.add("Wednesday");

          dayNames.add("Thursday");

          dayNames.add("Friday");

          dayNames.add("Saturday");

          days = dayNames.elements();

          while (days.hasMoreElements()){

             System.out.println(days.nextElement());

          }

       }

    }

    以上实例编译运行结果如下:

    Sunday

    Monday

    Tuesday

    Wednesday

    Thursday

    Friday

    Saturday

    4.2    位集合(BitSet)

    4.3    向量(Vector)

    import java.util.Vector;

    Vector<String> vect = new Vector(3,2);

    String s = new String("hello");

    vect.add("我们");

    vect.add("Hello");

    vect.addElement(s);

    for (int i=0;i<vect.size();i++) {

    System.out.println(vect.get(i));

    }

    • 栈(Stack)
    • 字典(Dictionary)
    • 哈希表(Hashtable)
    • 属性(Properties)
    • Collection 接口

    5       集合

    Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。

    Collection 接口存储一组不唯一,无序的对象。

     

    • List 接口

    List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。

    List 接口存储一组不唯一,有序(插入顺序)的对象。

    • Set

    Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。

    Set 接口存储一组唯一,无序的对象。

    • SortedSet

    继承于Set保存有序的集合。

    • Map

    Map 接口存储一组键值对象,提供key(键)到value(值)的映射。

    • Map.Entry

    描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。

    • SortedMap

    继承于 Map,使 Key 保持在升序排列。

    • Enumeration

    这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。

    5.1    Collection

    public class Person {

            private String name;

            private int age;

            public Person(String name, int age) {

                    this.name = name;

                    this.age = age;

            }

    @Override

            public String toString() {

                    return "Person [name=" + name + ", age=" + age + "]";

            }}

    import java.util.*;

    public class Test {

            public static void arrylist() {

                    Collection collection = new ArrayList();

                    //collection.add("hello");

                    //collection.add(12);

                    collection.add(new Person("Jack",42));

                    collection.add(new Person("Mary",19));

                    Person p1 = new Person("Tom",12);

                    collection.add(p1);

                    for (Object obj:collection) {

                           Person p = (Person)obj;

                           System.out.println(p.toString());   

                    }

                    System.out.println("=========================");

                    collection.remove(p1);

                    Iterator iter = collection.iterator();

                    while(iter.hasNext()) {

                           Person p = (Person) iter.next();

                           System.out.println(p.toString());

                    }

                    System.out.println(collection.contains(p1));

            }

            public static void main(String[] args) {

                    arrylist();

            }}

    结果:

    Person [name=Jack, age=42]

    Person [name=Mary, age=19]

    Person [name=Tom, age=12]

    =========================

    Person [name=Jack, age=42]

    Person [name=Mary, age=19]

    true

    5.1.1   线程安全

    ²  线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

    ²  线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

    ArrayList、HashSet、HashMap … 都不是线程安全的,Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

    List list = new ArrayList();

    List list2 = Collections.synchronizedList(list);

    5.1.2   排序

    List list3 = new ArrayList();

                    list3.add(new  Person("AA",13));

                    list3.add(new  Person("CC",3));

                    list3.add(new  Person("BB",32));

                    list3.add(new  Person("DD",14));

                    //按年龄升序

                    Collections.sort(list3,new Comparator() {

                           @Override

                           public int compare(Object o1,Object o2) {

                                   if(o1 instanceof Person && o2 instanceof Person) {

                                           Person p1 = (Person) o1;

                                           Person p2 = (Person) o2;

                                           return p1.getAge() - p2.getAge();

                                   }

                                   throw new ClassCastException("不能转为Person");

                           }

                    });

                    for(Object obj:list3) {

                           System.out.println(obj);

                    }

    结果:

    Person [name=CC, age=3]

    Person [name=AA, age=13]

    Person [name=DD, age=14]

    Person [name=BB, age=32]

    5.2    List

    • List 代表一个元素有序、且可重复的集合,集合中的每个元素都有其对应的顺序索引
    • List 允许使用重复元素,可以通过索引来访问指定位置的集合元素。
    • List 默认按元素的添加顺序设置元素的索引。

    List 集合里添加了一些根据索引来操作集合元素的方法:

    void add(int index, Object ele)

    boolean addAll(int index, Collection eles)

    Object get(int index)

    int indexOf(Object obj)

    int lastIndexOf(Object obj)

    Object remove(int index)

    Object set(int index, Object ele)

    List subList(int fromIndex, int toIndex)

    【Example】

    List list1 = Arrays.asList("A","B","C"); //返回只读List

                for (int i=0;i<list1.size();i++) {

                       System.out.println(list1.get(i));

                }

    5.2.1   LinkedList

    5.2.2   ArrayList

    import java.util.*;

    public class Test{

     public static void main(String[] args) {

         List<String> list=new ArrayList<String>();

         list.add("Hello");

         list.add("World");

         list.add("HAHAHAHA");

         //第一种遍历方法使用 For-Each 遍历 List

         for (String str : list) {   //也可以改写 for(int i=0;i<list.size();i++){list.get(i)} 这种形式

            System.out.println(str);

         }

         //第二种遍历,把链表变为数组相关的内容进行遍历

         String[] strArray=new String[list.size()];

         list.toArray(strArray);

         for(int i=0;i<strArray.length;i++) //这里也可以改写为  for(String str:strArray) 这种形式

         {

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

         }

        

        //第三种遍历 使用迭代器进行相关遍历

         Iterator<String> ite=list.iterator();

         while(ite.hasNext())//判断下一个元素之后有值

         {

             System.out.println(ite.next());

         }

     }

    }

    5.2.3   ArrayList和LinkedList的区别

    • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
    • 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
    • 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

    5.3    Set

    • Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败(还是保留之前的那一个元素)。
    • Set判断两个对象是否相同不是使用 == 运算符,而是根据equals方法

    5.3.1   HashSet

    HashSet是Set接口的典型实现,大多数时候使用Set集合是都使用这个实现类。

    HashSet具有以下特点:

    • 不能保证元素的排列顺序
    • HashSet不是线程安全的
    • 集合元素可以有一个null

    注:

    • 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
    • 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

    常用方法:

    返回值

    方法名 

    功能

     boolean 

    add(E e) 

    如果当前列表中不存在e, 则将e加入列表

     void 

    clear()

     从列表中删除所有元素

     boolean 

    contains(Object j)

    判断列表中是否有元素j

    Iterator<E>

    iterator()

    得到当前列表的遍历器

     boolean 

    remove(Object j)

    如果列表中存在元素j,则将其从列表中删除

    int

    size()

     得到列表中元素的个数

    【Example】

    import java.util.*;

    public class TestHashSet {

            public static void main(String[] args) {

             HashSet<String> hashset = new HashSet<String>();

    // Collection collection = new HashSet();

             //Set<String> hashset1 = new HashSet<String>();

             //Set hashset2 = new HashSet();

             hashset.add("Tom");

             hashset.add("Tom");

             hashset.add("Tom1");

             Iterator iterator = hashset.iterator();

             while(iterator.hasNext()) {

                     System.out.println(iterator.next());

             }

             System.out.println("============================");

             for(String string: hashset)System.out.println(string);

    }}

    结果:

            Tom

    Tom1

    ============================

    Tom

    Tom1

    5.3.2   LinkedHashSet

    LinkedHashSet 是 HashSet 的子类

    LinkedHashSet 集合根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

    LinkedHashSet 性能插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

    LinkedHashSet 不允许集合元素重复。

    //     Collection  linkedset = new LinkedHashSet();

    //     HashSet  linkedset = new LinkedHashSet();

    //     Set  linkedset = new LinkedHashSet();

            LinkedHashSet linkedset = new LinkedHashSet();

    5.3.3   TreeSet

    TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

    --Comparator comparator()

    --Object first()

    --Object last()

    --Object lower(Object e)

    --Object higher(Object e)

    --SortedSet subSet(fromElement, toElement)

    --SortedSet headSet(toElement)

    --SortedSet tailSet(fromElement)

    TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。

    //            TreeSet treeset = new TreeSet();

    //            Collection treeset = new TreeSet();

    //           Set<String> treeset = new TreeSet<String>();

                    Set treeset = new TreeSet();

                    treeset.add("Hello");

                    treeset.add("Tom");

                    treeset.add("Ton");

                    treeset.add("ABC");

                    System.out.println(treeset); //[ABC, Hello, Tom, Ton]

    5.3.3.1  自然排序

    • 排序:TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列
    • 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。
    • 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。
    • Comparable 的典型实现:

    –      BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较

    –      Character:按字符的 UNICODE 值来进行比较

    –      Boolean:true 对应的包装类实例大于 false 对应的包装类实例

    –      String:按字符串中字符的 UNICODE 值进行比较

    Date、Time:后边的时间、日期比前面的时间、日期大

    5.3.3.2  定制排序

    import java.util.Comparator;

    //1. 创建一个实现了Comparator接口的对象

            Comparator com = new Comparator() {

                    //向TreeSet中添加Customer类的对象,compare()方法中,

                    //指明按照Customer的哪个属性进行排序

                    @Override

                    public int compare(Object o1, Object o2) {

                           if(o1 instanceof Customer && o2 instanceof Customer ) {

                                   Customer c1 = (Customer)o1;

                                   Customer c2 = (Customer)o2;

                                   int i = c1.getId().compareTo(c2.getId());

                                   if(i == 0) {

                                           return c1.getName().compareTo(c2.getName());

                                   }

                                   return i;

                           }

                           return 0;

                    }

                   

            };

            //2.将此对象作为形参传递给TreeSet的构造器中

            TreeSet set = new TreeSet(com);

            //3.添加Comparator接口中的compare方法中涉及的类对象

            set.add(new Customer("AA",1003));

            set.add(new Customer("BB",1002));

            set.add(new Customer("GG",1004));

            set.add(new Customer("CC",1001));

            set.add(new Customer("DD",1007));

           

            for(Object obj : set) {

                    System.out.println(obj);

            }

    5.4    Map

    • Map 用于保存具有映射关系的数据,因此 Map 集合里保存着两组值,一组值用于保存 Map 里的 Key,另外一组用于保存 Map 里的 Value
    • Map 中的 key 和  value 都可以是任何引用类型的数据
    • Map 中的 Key 不允许重复,即同一个 Map 对象的任何两个 Key 通过 equals 方法比较中返回 false
    • Key 和 Value 之间存在单向一对一关系,即通过指定的 Key 总能找到唯一的,确定的 Value。

    常用方法:

    方法

    描述

    V put(K key, V value)

    可以相同的key值,但是添加的value值会覆盖前面的,返回值是前一个,如果没有就返回null

    remove()

    删除关联对象,指定key对象

    clear()

    清空集合对象

    values()

    获取所有值

    Set<K> keySet()

    返回Key对应的集合,Set类型

    value get(key)

    可以用于判断键是否存在的情况。当指定的键不存在的时候,返回的是null

    boolean isEmpty()

    长度为0返回true否则false

    boolean containsKey(Object key)

    判断集合中是否包含指定的key

    boolean containsValue(Object value)

    判断集合中是否包含指定的value

    int size()

    返回集合长度

    Map map = new HashMap();

    map.put("A","Hello1");

    map.put("B","Hello2");

            map.put("C","Hello3");

            System.out.println(map.keySet()); // [A, B, C]

            System.out.println(map.values()); // [Hello1, Hello2, Hello3]

            System.out.println(map.containsValue("Hello2")); // true

            Set set = map.keySet();

            System.out.println("============================");

            Iterator iterator = set.iterator();

            while(iterator.hasNext()) {

                    Object key = iterator.next();

                    Object val = map.get(key);

                    System.out.println(key+":"+val); // A:Hello1 B:Hello2 C:Hello3

            }

    【Example】

    import java.util.*;

    public class Test{

         public static void main(String[] args) {

              //Map map = new HashMap();

          Map<String, String> map = new HashMap<String, String>();

          map.put("1", "value1");

          map.put("2", "value2");

          map.put("3", "value3");

         

          //第一种:普遍使用,二次取值

          System.out.println("通过Map.keySet遍历key和value:");

          for (String key : map.keySet()) {

           System.out.println("key= "+ key + " and value= " + map.get(key));

          }

         

          //第二种

          System.out.println("通过Map.entrySet使用iterator遍历key和value:");

          Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();

          while (it.hasNext()) {

           Map.Entry<String, String> entry = it.next();

           System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());

          }

         

          //第三种:推荐,尤其是容量大时

          System.out.println("通过Map.entrySet遍历key和value");

          for (Map.Entry<String, String> entry : map.entrySet()) {

           System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());

          }

       

          //第四种

          System.out.println("通过Map.values()遍历所有的value,但不能遍历key");

          for (String v : map.values()) {

           System.out.println("value= " + v);

          }

         }

    }

    5.4.1   HashMap & Hashtable

    HashMap 和 Hashtable 是 Map 接口的两个典型实现类

    区别:

    • Hashtable 是一个古老的 Map 实现类,不建议使用
    • Hashtable 是一个线程安全的 Map 实现,但 HashMap 是线程不安全的。
    • Hashtable 不允许使用 null 作为 key 和 value,而 HashMap 可以
    • 与 HashSet 集合不能保证元素的顺序的顺序一样,Hashtable 、HashMap 也不能保证其中 key-value 对的顺序
    • Hashtable 、HashMap 判断两个 Key 相等的标准是:两个 Key 通过 equals 方法返回 true,hashCode 值也相等。
    • Hashtable 、HashMap 判断两个 Value相等的标准是:两个 Value 通过 equals 方法返回 true

    【Example】

            public static void main(String[] args) {

                           Map  map = new HashMap();

                           map.put("AA",new Person("Tom",12));

                           map.put("BB",new Person("Tom1",13));

                           map.put("DD",new Person("Tom2",14));

                           map.put("CC",new Person("Tom3",15));

                           Iterator iterator = map.entrySet().iterator();

                           //Iterator iterator = map.keySet().iterator();  // AA BB DD CC

                           while(iterator.hasNext()) {

                                   System.out.println(iterator.next());

            }}

    结果:

            AA=Person [name1=Tom, age=12]

    BB=Person [name1=Tom1, age=13]

    DD=Person [name1=Tom2, age=14]

    CC=Person [name1=Tom3, age=15]

    5.4.2   TreeMap

    • TreeMap 存储 Key-Value 对时,需要根据 Key 对 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。

    TreeMap 的 Key 的排序

    ²  自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException

    ²  定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

    【Example】

    public static void main(String[] args) {

                           Comparator comparator = new Comparator() {

                                   @Override

                                   public int compare(Object o1,Object o2) {

                                           if(o1 instanceof Person && o2 instanceof Person) {

                                                   Person p1 = (Person)o1;

                                                   Person p2 = (Person)o2;

                                                   int nameFlag =  p1.getName().compareTo(p2.getName());

                                                   if(nameFlag == 0) {

                                                           return  p1.getAge() - p2.getAge();

                                                   }else {

                                                           return nameFlag;

                                                   }

                                           }

                                           return 0;

                                   }

                           };

                           Map map = new TreeMap(comparator);

                           map.put(new Person("AA",120),"AA");

                           map.put(new Person("AA",13),"BB");

                           map.put(new Person("DD",16),"DD");

                           map.put(new Person("CC",15),"CC");

    //                    Iterator iterator = map.entrySet().iterator();

                           Iterator iterator = map.keySet().iterator();

                           while(iterator.hasNext()) {

                                   Object key = iterator.next();

                                   Object val = map.get(key);

                                   System.out.println(((Person)key).getName()+":"+key);

                           }

    }

    结果:

    AA:Person [name=AA, age=13]

    AA:Person [name=AA, age=120]

    CC:Person [name=CC, age=15]

    DD:Person [name=DD, age=16]

    5.4.3   LinkedHashMap

    • LinkedHashMap 是 HashMap 的子类
    • LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

    public static void main(String[] args) {

                    Map map = new LinkedHashMap();

                    map.put("key1", "value1");

                    map.put("key4", "value4");

                    map.put("key2", "value2");

                    map.put("key3", "value3");

    //            Iterator iterator = map.entrySet().iterator();

                    Iterator iterator = map.keySet().iterator();

                    while(iterator.hasNext()) {

                           Object key = iterator.next();

                           Object val = map.get(key);

                           System.out.println(key + ":"+ val);

                    }

            }}

    结果:

    key1:value1

    key4:value4

    key2:value2

    key3:value3

    5.4.4   Properties

    ²  Properties 类是 Hashtable 的子类,该对象用于处理属性文件

    ²  由于属性文件里的 key、value 都是字符串类型,所以 properties 里的 Key 和 Value 都是字符串类型的

    【Example】

    public static void main(String[] args) throws IOException{

                    Properties properties = new  Properties();

                    InputStream inStream = PropertiesTest.class.getClassLoader()

                                   .getResourceAsStream("jdbc.properties");

                    properties.load(inStream);

                    String user = properties.getProperty("user");

                    String password = properties.getProperty("password");

                    System.out.println("user:"+user);

                    System.out.println("password:"+password);

    }

    结果:

            user:root

    password:123456

    5.5    Enumeration

    mport java.util.Vector;

    import java.util.Enumeration;

    public class EnumerationTester {

       public static void main(String args[]) {

          Enumeration<String> days;

          Vector<String> dayNames = new Vector<String>();

          dayNames.add("Sunday");

          dayNames.add("Monday");

          dayNames.add("Tuesday");

          dayNames.add("Wednesday");

          dayNames.add("Thursday");

          dayNames.add("Friday");

          dayNames.add("Saturday");

          days = dayNames.elements();

          while (days.hasMoreElements()){

             System.out.println(days.nextElement());

          }

       }

    }

    6       泛型

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

    不使用泛型时:

    ²  集合中的类型并不安全,可以向集合中放入任何引用类型的对象

    ²  从集合中取出的对象都是Object类型,在具体操作时可能需要进行类型的强制转换,那么再强制类型转换时也容易发生ClassCastException

    【Example】

    class Person{

            private String name;

            private int age;

            public Person(String name, int age) {

                    this.name = name;

                    this.age = age;

            }

            public String getName() {

                    return name;

            }

            public int getAge() {

                    return age;

            }

            @Override

            public String toString() {

                    return "Person [name=" + name + ", age=" + age + "]";

            }

    }

    public static void main(String[] args) {

                    List<Person> person = new ArrayList<Person>();

                    person.add(new Person("Tom1",21));

                    person.add(new Person("Tom2",31));

                    person.add(new Person("Tom3",27));

                    for (Person p:person) {

                           System.out.println(p.getName());

                    }

                    Set<Person> persons = new TreeSet<>(new Comparator<Person>() {

                    @Override

                    public int compare(Person o1,Person o2) {

                           return o1.getAge() - o2.getAge();

                    }

            });

            persons.add(new Person("Tom1",21));

            persons.add(new Person("Tom2",31));

            persons.add(new Person("Tom3",27));

            Iterator<Person> iterator = persons.iterator();

            while(iterator.hasNext()) {

                    System.out.println(iterator.next());

            }

            Map<String,Person> personMap = new HashMap<>();

            personMap.put("AA1",new Person("AA",12));

            personMap.put("BB2",new Person("BB",12));

            personMap.put("CC3",new Person("CC",12));

            personMap.put("DD4",new Person("DD",12));

            for(Map.Entry<String, Person> entry:personMap.entrySet()) {

                    String key = entry.getKey();

                    Person p =entry.getValue();

                    System.out.println(key+"--"+p);

            }

    }

    结果:

            Tom1

    Tom2

    Tom3

    Person [name=Tom1, age=21]

    Person [name=Tom3, age=27]

    Person [name=Tom2, age=31]

    AA1--Person [name=AA, age=12]

    BB2--Person [name=BB, age=12]

    CC3--Person [name=CC, age=12]

    DD4--Person [name=DD, age=12]

    6.1    带通配符的集合声明

    class Person{}

    class Student extends Person{}

    //public void printPerson(List<?> obj) 任意类型

    public void printPersons(List<? extends Person> person) {  //必须是继承Person的类型

                    System.out.println("=====printPersons=====");

                    person.add(new Person()) //只要存在通配符,写入就是非法的

    }

    public static void main(String[] args) {

                    Test test = new Test();

                    List<Person> personlist = new ArrayList<>();

                    List<Student> studentList = new ArrayList<>();

                    test.printPersons(studentList);

    }

    6.2    泛型类

    泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。

    和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

    【Example】

    public class Box<T> {

      

      private T t;

      public void add(T t) {

        this.t = t;

      }

      public T get() {

        return t;

      }

      public static void main(String[] args) {

        Box<Integer> integerBox = new Box<Integer>();

        Box<String> stringBox = new Box<String>();

        integerBox.add(new Integer(10));

        stringBox.add(new String("菜鸟教程"));

        System.out.printf("整型值为 :%d ", integerBox.get());

        System.out.printf("字符串为 :%s ", stringBox.get());

      }

    }

    6.3    泛型方法

    你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

    下面是定义泛型方法的规则:

    • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
    • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
    • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
    • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)

    【Example】

    public class Dao <T>{

            public T get(int id) {

                    T result = null;

                    return result;

            }

    //     在泛型类中使用类声明的泛型

            public void save(T entity) {

            }

           

            /*

             * 在类中使用泛型方法:

             * 1.在方法的返回值前面使用<>声明

             * 泛型类型, 则在方法的返回值、参数、方法体中都可以使用该类型

             */

            public <E>  E  getProperty(int id) {

                    E result = null;

                    return result;

            }

            public <E> void test(E entity) {

                   

            }

           

            public static void main(String[] args) {

           

                    Dao<Person> dao  = new Dao<>();

                    Person person = dao.get(1);

                    String name2 = dao.getProperty(1);

                    int age = dao.getProperty(2);

                    dao.test("");

                    dao.test(new Person("Tom",12));

            }

    }

    7       枚举类

    在某些情况下,一个类的对象是有限而且固定的。

    7.1    自定义枚举类

    • 私有化构造器
    • 属性使用final来修饰
    • 在类的内部创建枚举类的对象,且使用public static final修饰

    public class Season {

            private  final String SEASON_NAME;

            private  final String SEASON_DESC;

            private Season(String seasonName,String seasonDesc) {

                    this.SEASON_NAME = seasonName;

                    this.SEASON_DESC = seasonDesc;

            }

            @Override

            public String toString() {

                    return "Season [SEASON_NAME=" + SEASON_NAME + ", SEASON_DESC=" + SEASON_DESC + "]";

            }

            final static Season SPRING = new Season("春天","春天来了");

            final static Season SUMMER = new Season("夏天","夏天到了");

            final static Season AUTUMN = new Season("秋天","秋天来了");

            final static Season WINTER = new Season("冬天","冬天到了");

            public static void main(String[] args) {

                    System.out.println(Season.SPRING);

                    System.out.println(Season.SUMMER);

                    System.out.println(Season.AUTUMN);

                    System.out.println(Season.WINTER);

            }

    }

    结果:

    Season [SEASON_NAME=春天, SEASON_DESC=春天来了]

    Season [SEASON_NAME=夏天, SEASON_DESC=夏天到了]

    Season [SEASON_NAME=秋天, SEASON_DESC=秋天来了]

    Season [SEASON_NAME=冬天, SEASON_DESC=冬天到了]

    7.2    使用 enum 定义枚举类

    枚举类和普通类的区别

    • 使用 enum 定义的枚举类默认继承了 java.lang.Enum 类
    • 枚举类的构造器只能使用 private 访问控制符
    • 枚举类的所有实例必须在枚举类中显式列出(, 分隔    ; 结尾). 列出的实例系统会自动添加 public static final 修饰
    • 所有的枚举类都提供了一个 values 方法, 该方法可以很方便地遍历所有的枚举值

    枚举类的属性

    • 枚举类对象的属性不应允许被改动, 所以应该使用 private final 修饰
    • 枚举类使用 private final 修饰的属性应该在构造器中为其赋值
    • 若枚举类显式的定义了带参数的构造器, 则在列出枚举值时也必须对应的传入参数

    7.2.1   枚举类实现

    import java.lang.Enum;

    public enum  Season2 {

    //枚举类的实例需要在枚举类的第一列列出

            SPRING("春天","春天来了"),

            SUMMER ("夏天","夏天到了"),

            AUTUMN ("秋天","秋天来了"),

            WINTER ("冬天","冬天到了");

           

            final String SEASON_NAME;

            final String SEASON_DESC;

            private Season2(String seasonName,String seasonDesc) {

                    this.SEASON_NAME = seasonName;

                    this.SEASON_DESC = seasonDesc;

            }

            public static void main(String[] args) {

                    System.out.println(Season2.AUTUMN);

                    System.out.println(Season2.SUMMER);

                    System.out.println(Season2.AUTUMN);

                    System.out.println(Season2.WINTER);

                    System.out.println("=======================");

                    Season2 [] seasonList = Season2.values();

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

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

                    }

                    System.out.println("=======================");

                    for(Season2 season2:Season2.values()) {

                           System.out.println(season2);

                    }

                    System.out.println("=======================");

    //     把一个字符串转换为对应得枚举类对象

                    String input = "SPRING";

                    Season2 s = Enum.valueOf(Season2.class, input);

                    System.out.println(s);

            }}

    结果:

    AUTUMN

    SUMMER

    AUTUMN

    WINTER

    =======================

    SPRING

    SUMMER

    AUTUMN

    WINTER

    =======================

    SPRING

    SUMMER

    AUTUMN

    WINTER

    =======================

    SPRING

    7.2.2   实现接口的枚举类

    • 和普通 Java 类一样枚举类可以实现一个或多个接口
    • 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式, 则可以让每个枚举值分别来实现该方法

    public interface Info {

            String getInfo();

    }

    【Example1】

    package com.enum1;

    public enum Season3 implements Info {

            SPRING("春天","春天来了"),

            SUMMER ("夏天","夏天到了"),

            AUTUMN ("秋天","秋天来了"),

            WINTER ("冬天","冬天到了");

           

             final String SEASON_NAME;

             final String SEASON_DESC;

            private Season3(String seasonName,String seasonDesc) {

                    this.SEASON_NAME = seasonName;

                    this.SEASON_DESC = seasonDesc;

            }

            @Override

             public String getInfo() {

                    switch(this) {

                    case SPRING: return "A";

                    case SUMMER: return "B";

                    case AUTUMN: return "C";

                    case WINTER: return "D";

                    }

                    return null;

            }

            public static void main(String[] args) {

                    System.out.println(Season3.WINTER.getInfo()); //D

            }

    }

    【Example2】

    package com.enum1;

    public enum Season4 implements Info {

            SPRING("春天","春天来了"){

                    @Override

                     public String getInfo() {return "A";}

            },

             SUMMER ("夏天","夏天到了"){

                    @Override

                     public String getInfo() {return "B";}

            },

             AUTUMN ("秋天","秋天来了"){

                    @Override

                     public String getInfo() {return "C";}

            },

             WINTER ("冬天","冬天到了"){

                    @Override

                     public String getInfo() {return "D";}

            };

           

             final String SEASON_NAME;

             final String SEASON_DESC;

            private Season4(String seasonName,String seasonDesc) {

                    this.SEASON_NAME = seasonName;

                    this.SEASON_DESC = seasonDesc;

            }

            public static void main(String[] args) {

                    System.out.println(Season4.WINTER.getInfo()); //D

            }

    }

    8       注解(Annotation)

    • Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理. 通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息.
    • Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明

    8.1    基本的 Annotation

    使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用. 用于修饰它支持的程序元素

    三个基本的 Annotation:

    • @Override: 限定重写父类方法, 该注释只能用于方法
    • @Deprecated: 用于表示某个程序元素(类, 方法等)已过时
    • @SuppressWarnings: 抑制编译器警告.

    8.2    自定义 Annotation

    ü  定义新的 Annotation 类型使用 @interface 关键字

    ü  Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明. 其方法名和返回值定义了该成员的名字和类型.

    ü  可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始值可使用 default 关键字

    ü  没有成员定义的 Annotation 称为标记; 包含成员变量的 Annotation 称为元数据 Annotation

    【Example】

    //     使用@interface 来声明注解

    public @interface HelloAnnotation {

    //     使用接口中声明方法的方式来声明注解的属性

            String major();

            int age();

            String school() default "boye";

    }

    【Example】

    @HelloAnnotation(major = "hello",age = 12,school="龙岗中学")

    public class TestAnnotation {

            @HelloAnnotation(major = "hello",age = 12)

            public void test() {

                    System.out.println("test ...");

            }

    }

    ü  @Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 使用 @Rentention 时必须为该 value 成员变量指定值

    如:

    @Retention(RetentionPolicy.CLASS): 编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注释. 这是默认值

    @Retention(RetentionPolicy.RUNTIME):编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注释. 程序可以通过反射获取该注释

    @Retention(RetentionPolicy.SOURCE): 编译器直接丢弃这种策略的注释

    ü  @Target: 用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素. @Target 也包含一个名为 value 的成员变量.

    ü  @Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.

    ü  @Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注释

    【Example】

    // 适用类、接口(包括注解类型)或枚举

    import java.lang.annotation.Retention;

    import java.lang.annotation.Target;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.ElementType;

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.TYPE)

    public @interface ClassInfo {

        String value();

    }

    // 适用field属性,也包括enum常量

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.FIELD)

    public @interface FieldInfo {

        int[] value();

    }

    // 适用方法

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD)

    public @interface MethodInfo {

        String name() default "long";

        String data();

        int age() default 27;

    }

    @ClassInfo(value = "hello")

    public class TestAnnotation {

           

    }

    9       IO

    • 输入:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
    • 输出:将程序(内存)数据输出到磁盘、光盘等存储设备中
    • Java 的 IO 流主要包括输入、输出两种 IO 流,每种输入、输出流有可分为字节流和字符流两大类:

    字节流以字节为单位来处理输入、输出操作

    字符流以字符为单位来处理输入、输出操作

    9.1    File 类

    • File 类代表与平台无关的文件和目录。
    • File  能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流

    import java.io.File;

    import java.io.IOException;

    public static void main(String[] args) throws IOException {

                    File file = new File("E:/java/com/src/test");

    //            File file1 = new  File("test1.txt");

                    System.out.println(file.exists());

                    System.out.println(file.isFile());

                    System.out.println(file.isDirectory());

                    System.out.println(file.getName());

    //            file.renameTo(new File("test.txt"));

    //            file.delete();

                    file.mkdir();

                    File file2 = new File("E:/java/com/src/test/test.txt");

                    file2.createNewFile();

                    System.out.println(file.list());

            }

    9.2    InputStream & Reader

    • InputStream 和 Reader 是所有输入流的基类。
    • InputStream(典型实现:FileInputStream):

    int read()

    int read(byte[] b)

    int read(byte[] b, int off, int len)

    • Reader(典型实现:FileReader):

    int read()

    int read(char [] c)

    int read(char [] c, int off, int len)

    • 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。

    import java.io.InputStream;

    import java.io.Reader;

    import java.io.FileInputStream;

    import java.io.FileReader;

    import java.io.IOException;

    【InputStream】

    public  void inputStreamTest() throws IOException{

                    InputStream in = new FileInputStream("E:\java\com\src\test.txt");

    //            int result = -1;

    //            while( (result=in.read()) != -1) {

    //                    System.out.print((char)result);

    //            }

                   

                    byte[] buffer = new byte[1024 * 10];

                    int len = in.available();

                    in.read(buffer,0,len);

                    for(int i=0;i<len;i++) {

                           System.out.print((char)buffer[i]); // hello ok ??????

                    }

                    in.close();

                   

            }

    【Reader】

    public void readerTest() throws IOException{

                    Reader in = new FileReader("E:\java\com\src\test.txt");

                    char[] buffer = new char[1024 * 10];

                    int len = in.read(buffer);

                    System.out.println(len);

                    for(int i=0;i<len;i++) {

                           System.out.print(buffer[i]);  //hello ok 我很好

                    }

    }

    9.3    OutputStream & Writer

    • OutputStream:

    void write(int b)

    void write(byte[] buff)

    void write(byte[] buff, int off, int len);

    • 因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数

    void write(int c)

    void write(char[] buff)

    void write(char[] buff, int off, int len);

    void write(String str);

    void write(String str, int off, int len)

    注:若文件不存在会创建

    【Example

    import java.io.OutputStream;

    import java.io.Writer;

    import java.io.FileOutputStream;

    import java.io.FileWriter;

    import java.io.IOException;

     

    public void writer() throws IOException {

                    Writer writer = new FileWriter("E:\java\com\src\test.txt");

                    String content = "https://www.runoob.com/python3/python3-built-in-functions.html";

                    writer.write(content);

                    writer.close();

    }

    public void outStream() throws IOException {

                  OutputStream out = new FileOutputStream("E:\java\com\src\test1.txt");

                  String content = "https://www.runoob.com/python3/python3-built-in-functions.html";

                  byte [] byte_content = content.getBytes();

                  out.write(byte_content);

                  out.close();

    }

    【Copy】

    import java.io.InputStream;

    import java.io.OutputStream;

    import java.io.Reader;

    import java.io.Writer;

    import java.io.FileInputStream;

    import java.io.FileOutputStream;

    import java.io.FileReader;

    import java.io.FileWriter;

    import java.io.IOException;

    //               字节输出流复制

    public void copyByteFile (String ScrFile,String DesFile)  throws IOException{

                    InputStream in = new FileInputStream(ScrFile);

                    OutputStream out = new FileOutputStream(DesFile);

                    byte[] buffer = new byte[10];

                    int result = 0;

                    while((result = in.read(buffer)) != -1) {

                           out.write(buffer,0,result);

                    }

                    in.close();

                    out.close();

    }

    //字符输出流复制

    public void copyCharFile(String ScrFile,String DesFile)  throws IOException{

                      Reader reader  = new FileReader(ScrFile);

                      Writer writer = new FileWriter(DesFile);

                      int result = 0;

    //               char [] buffer = new char[10];

    //               while((result = reader.read(buffer)) != -1) {

    //                    writer.write(buffer,0,result);

    //               }

                      while((result=reader.read()) !=-1) {

                           writer.write(result);

                      }

                      reader.close();

                      writer.close();

    }

    9.4    缓冲流Buffered

    BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

    import java.io.InputStream;

    import java.io.OutputStream;

    import java.io.Reader;

    import java.io.Writer;

    import java.io.BufferedInputStream;

    import java.io.BufferedOutputStream;

    import java.io.BufferedReader;

    import java.io.BufferedWriter;

    import java.io.FileInputStream;

    import java.io.FileOutputStream;

    import java.io.FileReader;

    import java.io.FileWriter;

    import java.io.IOException;

    //字符输出流复制

            public void copyBufferedFile(String ScrFile,String DesFile)  throws IOException{

                    Reader reader  = new FileReader(ScrFile);

                    BufferedReader readerbuf = new BufferedReader(reader);

                    Writer writer = new FileWriter(DesFile);

                    BufferedWriter writerbuf = new BufferedWriter(writer);

                          

                    String str = null;

                    for(int i=0;(str =readerbuf.readLine() ) != null;i++) {

                           if(i !=0 ) { //若最后一行为空不会读取

                                   writerbuf.write(" ");

                           }

                           writerbuf.write(str);

                    }

                    readerbuf.close();

                    writerbuf.close();

            }

                   

    //            字节输出流复制

            public void copyStreamBufferedFile(String ScrFile,String DesFile)  throws IOException{

                    InputStream in = new FileInputStream(ScrFile);

                    BufferedInputStream inbuf = new BufferedInputStream(in);

                    OutputStream out = new FileOutputStream(DesFile);

                    BufferedOutputStream outbuf = new BufferedOutputStream(out);

                    byte [] buffer = new byte[10];

                    int result = 0;

                    while((result = inbuf.read(buffer)) != -1) {

                           outbuf.write(buffer,0,result);

                    }

                          

                    inbuf.close();

                    outbuf.close();

            }

    9.5    转换流

    public void streamToReader() throws IOException{

                    InputStream in = new FileInputStream("E:\java\com\src\test.txt");

                    Reader reader  = new InputStreamReader(in);

                    BufferedReader bufReader = new BufferedReader(reader);

                   

                   OutputStream out = new FileOutputStream("E:\java\com\src\test11.txt");

                    Writer writer = new OutputStreamWriter(out);

                    BufferedWriter bufWriter = new BufferedWriter(writer);

                   

                    String str = null;

                    char [] buffer = new char[10];

                    while((str = bufReader.readLine()) != null) {

                           System.out.println(str);

                           bufWriter.write(str+" ");

                    }

                    in.close();

                    reader.close();

                    bufReader.close();

                   

                    bufWriter.close();

                    writer.close();

                    out.close();

                   

            }

    9.6    使用对象流序列化对象

    • 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:

    创建一个 ObjectOutputStream

    调用 ObjectOutputStream 对象的 writeObject() 方法输出可序列化对象

    • 反序列化

    创建一个 ObjectInputStream

    调用 readObject() 方法读取六种的对象

    • 如果某个类的字段不是基本数据类型或 String  类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化

    import java.io.Serializable;

    public class Person implements Serializable{

           

    //     类的版本号,用于对象的序列化,具体用于读取对象时比对硬盘上对象的版本和

    //     程序中对象的版本是否一致,若不一致读取失败,并抛出异常

            private static final long serialVersionUID = 1L;

            private String name;

            private int age;

            @Override

            public String toString() {

                    return "Person [name=" + name + ", age=" + age + "]";

            }

            public String getName() {

                    return name;

            }

            public Person(String name, int age) {

                    this.name = name;

                    this.age = age;

            }

            public Person() {

            }

    }

    【序列化】

    import java.io.FileOutputStream;

    import java.io.IOException;

    import java.io.ObjectOutputStream;

    import java.io.OutputStream;

    public static void main(String[] args) throws IOException {

                    Person p = new Person("Tom",12);

                    System.out.println(p.toString());

                    OutputStream out = new FileOutputStream("E:\java\com\src\obj.txt");

                    ObjectOutputStream outObj = new ObjectOutputStream(out);

                    outObj.writeObject(p);

                    out.close();

                    outObj.close();

    }

    【反序列化】

    import java.io.FileInputStream;

    import java.io.IOException;

    import java.io.ObjectInputStream;

    import java.io.InputStream;

    public static void main(String[] args) throws IOException, ClassNotFoundException {

            InputStream in =  new FileInputStream("E:\java\com\src\obj.txt");

            ObjectInputStream inObj = new ObjectInputStream(in);

            Person p = (Person)inObj.readObject();

            System.out.println(p.getName());

            System.out.println(p);

    }

    9.7    RandomAccessFile 类

    • RandomAccessFile 类既可以读取文件内容,也可以向文件输出数据
    • RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读写文件

    支持只访问文件的部分内容

    可以向已存在的文件后追加内容

    • RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:

    long getFilePointer():获取文件记录指针的当前位置

    void seek(long pos):将文件记录指针定位到 pos 位置

    • 创建 RandomAccessFile 类可以指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:

    r: 以只读方式打开

    rw:以读、写方式打开

    • 创建 RandomAccessFile 类可以指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:

    r: 以只读方式打开

    rw:以读、写方式打开

    import java.io.IOException;

    import java.io.RandomAccessFile;

    public void randomAccessFile() throws IOException{

    RandomAccessFile access = new RandomAccessFile("E:\java\com\src\test.txt","rw");

    String str = null;

    while((str = access.readLine()) != null) {

            System.out.println(str);

    }

    access.writeBytes("hello");

    access.close();

                   

    }

    10            反射

    10.1 Class 类

    • 对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
    • Class 对象只能由系统建立对象
    • 一个类在 JVM 中只会有一个Class实例
    • 每个类的实例都会记得自己是由哪个 Class 实例所生成

    获取class对象的3种方式:

    方式

    描述

    Class clazz = null;

    clazz =Person.class;

    直接通过类名.class的方式得到

    Object obj1 = new Person("Tom",12);

    Class clazz1 = obj1.getClass();

    通过对象调用getClass()方法来获取

    String className = "com.classTest.Person";

    Class clazz2 = Class.forName(className);

    通过全类名的方式获取,用的较多

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

                   

                   

    //            获取class对象的3种方式:

    //            1.1直接通过类名.class的方式得到

                    Class clazz = null;

                    clazz =Person.class;

    //            1.2通过对象调用getClass()方法来获取

                    Object obj1 = new Person("Tom",12);

                    Class clazz1 = obj1.getClass();

                   

    //            1.3通过全类名的方式获取,用的较多

                    String className = "com.classTest.Person";

                    Class clazz2 = Class.forName(className);

                   

    //            利用Class 对象newInstance()方法来创建类的一个对象

    //            实际调用的是类的那个无参数的构造器!

    //            一般情况,一个类若声明了带参数的构造器,也要声明一个无参数的构造器。

                    Object obj2 = clazz2.newInstance();

                   

                    Method[] methods = clazz.getDeclaredMethods();

                    Field[] fields = clazz.getDeclaredFields();

                    for (Method m:methods) {

                           System.out.println(m);

                    }

                    System.out.println("=========================");

                    for (Field f:fields) {

                           System.out.println(f);

                    }

            }

    10.2 ClassLoader(类加载器)

    重要方法:in = this.getClass().getClassLoader().getResourceAsStream("com/test/jdbc.properties");

    类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

    public static void main(String[] args) throws ClassNotFoundException {

    //            1.获取一个系统的类加载器

                    ClassLoader classLoader = ClassLoader.getSystemClassLoader();

                    System.out.println(classLoader);

    //            2.获取系统类加载器的父类加载器

                    classLoader = classLoader.getParent();

                    System.out.println(classLoader);

    //            3.获取扩展类加载器的父类加载器

                    classLoader = classLoader.getParent();

                    System.out.println(classLoader);

                   

                    String className = "com.classTest.Person";

                    classLoader = Class.forName(className).getClassLoader();

                    System.out.println(classLoader);

                   

                    classLoader = Class.forName("java.lang.Object").getClassLoader();

                    System.out.println(classLoader);

           

            }

            public  void test() {

    //            调用getResourceAsStream 获取类路径下的文件对应的输入流

                    InputStream in = null;

                    in = this.getClass().getClassLoader().getResourceAsStream("com/test/jdbc.properties");

            }

    10.3 反射_概述

    • 反射概述

         Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。

    • Java反射机制主要提供了以下功能:

    ·在运行时构造任意一个类的对象

    ·在运行时获取任意一个类所具有的成员变量和方法

    ·在运行时调用任意一个对象的方法(属性)

    ·生成动态代理

    对象方法

    描述

    String className = "com.classTest.Person";

    Class clazz = Class.forName(className);

    加载类

    Class superClazz = clazz.getSuperclass();

    获取当前类的父类

    Object obj =clazz.newInstance();

    在运行时构造一个类的对象

    Method[] method = clazz.getDeclaredMethods();

    获取所有方法,包括private方法且只获取当前类声明的方法(继承的不算)

    Field [] fields = clazz.getDeclaredFields()

    获取所有属性,包括private属性且只获取当前类声明的属性(继承的不算)

    Method method =null;

    clazz.getDeclaredMethod("setName",String.class);

    获取指定方法[包括private方法],参数[为方法名,参数类型]

    Field  field = clazz.getDeclaredField("age");

    获取指定Field

    method.invoke(obj, "Tom");不能执行private方法

    执行方法,参数(对象,方法名)

    Method m = clazz.getDeclaredMethod("test");

    m.setAccessible(true);

    m.invoke(obj);

    设置为可被访问,私有方法也可被执行

    10.4 反射_Method

    public class R {

           

    public Object invoke(Object obj,String methodName, Object ... args) {

    //            1.获取Method对象

                    Class [] parameterTypes = new Class[args.length];

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

                           parameterTypes[i] = args[i].getClass(); //获取参数类型

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

                    }

                    try {

    //     获取指定方法,[参数类型]

    Method  method = obj.getClass().

    getDeclaredMethod(methodName, parameterTypes);

                                   return method.invoke(obj, args);

                           } catch (Exception e) {

                                   e.printStackTrace();

                           }

                           return null;

            }

           

    public Object invoke(String className,String methodName, Object ... args)  {

    //            1.获取Method对象

                    Class [] parameterTypes = new Class[args.length];

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

                           parameterTypes[i] = args[i].getClass();

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

                    }

                    try {

                                   Object obj = Class.forName(className).newInstance();

                                   Method  method = obj.getClass().

    getDeclaredMethod(methodName, parameterTypes);

                                   return method.invoke(obj, args);

                           } catch (Exception e) {

                                   e.printStackTrace();

                           }

                           return null;

            }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

                    String className = "com.classTest.Person";

                    Class clazz = Class.forName(className);

    //            获取所有方法,包括private方法,且只获取当前类声明的方法

                    Method [] methods = clazz.getDeclaredMethods();

                    for(Method m:methods) {

                           System.out.println(m.getName());

                    }

    //            获取指定方法,参数【为方法名,参数类型】

                    Method method = clazz.getDeclaredMethod("setName",String.class);

                    System.out.println(method);

                   

                    Method method1 = clazz.getDeclaredMethod("getName");

                    System.out.println(method1);

                   

    //            调用缺省构造函数,返回该Class对象的一个实例

                    Object obj = clazz.newInstance();

    //            执行方法

                    method.invoke(obj, "Tom");

                    System.out.println(method1.invoke(obj)); //Tom

                    Object ret = clazz.getDeclaredMethod("toString").invoke(obj);

                    System.out.println(ret);  //Person [name=Tom, age=10]

                   

                    R r = new R();

                    Person p = new Person();

                    System.out.println(r.invoke(p, "setName","Tom1",12));

                    System.out.println(p.toString());

                   

                    System.out.println(r.invoke("com.classTest.Person", "toString"));

                   

                    Class clazz3 = Person.class;

                    Object obj3 = clazz3.newInstance();

                    r.invoke(obj3, "setName","Tom13",13);

                    System.out.println(obj3.toString());

            }

    }

    10.4.1 执行类中(子类或父类 )私有方法或公有方法

    //获取clazz的methodName方法,该方法可能是私有方法, 还可能在父类中(私有方法)

            public Method  getMethod(Class clazz,String methodName, Class ... parameterTypes)  {

                    for(;clazz !=Object.class; clazz = clazz.getSuperclass()) {

                            try {

                                   Method method = clazz.getDeclaredMethod(methodName, parameterTypes);

                                   System.out.println(method);

                                   return method;

                            } catch (Exception e) {}

                    }

                    return null;

    }

    public class Student  extends Person {

            private void method1(String name,Integer age) {

                    System.out.println("private void method1 name:"+name+" age:"+age);

            }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

            Class clazz = Class.forName("com.classTest.Student");

            Method method = null;

            String methodName ="method1";

            Object[] params = {"Tom",23};

            Class[] paramType = new Class[params.length];

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

                    paramType[i] = params[i].getClass();

            }

            for(;clazz !=Object.class; clazz = clazz.getSuperclass()) {

                    try {

                           method = clazz.getDeclaredMethod(methodName,paramType);

                           System.out.println(method);

                           break;

                    }catch(Exception e) {}

            }

            Object obj = clazz.newInstance();

            method.setAccessible(true); //设置为可被执行,当该方法为私有方法时也会执行

            method.invoke(obj,params);

    }

    结果:

    private void com.classTest.Student.method1(java.lang.String,java.lang.Integer)

    Constructor Person() ...

    private void method1 name:Tom age:23

    10.5 反射_Field

    方法

    描述

    Class clazz = null;

    clazz=Class.forName("com.test.Person");

    加载类

    clazz.getDeclaredFields();

    获取当前类Fields 的数组,包括private修饰的,(继承的不算)

    Field  field = clazz.getDeclaredField("age");

    获取指定名字的Field,包括private修饰的

    Object val = field.get(new Person("Tom",12));

    获取指定对象的Field的值(public)

    Field field2 = clazz.getDeclaredField("name");

    Person person = new Person("Tom",12);

    field2.setAccessible(true);

    Object val2 = field2.get(person);

    获取私有的(private)指定对象Field的值

                    Class clazz = Class.forName("com.classTest.Person");

    //            获取Fields 的数组,包括private修饰的

                    Field [] fields = clazz.getDeclaredFields();

                    for(Field f:fields) {

                           System.out.println(f);

                    }

    //            获取指定名字的Field

                    Field  field = clazz.getDeclaredField("age");

                    System.out.println(field);

                   

                    Person person = new Person("Tom",12);

    //            获取指定对象的Field的值(public)

                    Object val = field.get(person);

                    System.out.println(val);

    //            设置指定对象的Field的值

                    field.set(person,13);

                    System.out.println(person.toString());

    //            若该字段是私有的,需要调用setAccessible(true)方法

                    Field field2 = clazz.getDeclaredField("name");

                    field2.setAccessible(true);

                    Object val2 = field2.get(person);

                    System.out.println(val2);

    【Example2】

    Class clazz = Class.forName("com.classTest.Student");

    String fieldName = "age";

    Object val = 12;

    Field field = null;

    Object obj = null;

    for(Class clazz2 = clazz ;clazz2 != Object.class;clazz2 = clazz2.getSuperclass()) {

            try {

                    field = clazz2.getDeclaredField(fieldName);

                    break;

            }catch(Exception e) {}

    }

    obj = clazz.newInstance();

    field.setAccessible(true);

    field.set(obj,val);

    System.out.println(field.get(obj));

    10.6 反射_Constructor

    String className="com.classTest.Person";

                   

                    //获取构造器对象

                    Constructor<Person> [] constructors =

                                   (Constructor<Person>[]) Class.forName(className).getConstructors();     

                    for(Constructor<Person> constructor:constructors) {

                           System.out.println(constructor);

                    }

                   

                    Class<Person> clazz = (Class<Person>) Class.forName(className);

                    Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);

                    System.out.println(constructor);

                   

    //调用构造器newInstance()方法创建对象

                    Object obj = constructor.newInstance("Tom",12);

    10.7 反射_Annotation

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    @Retention(RetentionPolicy.RUNTIME)

    @Target(value= {ElementType.METHOD})

    public @interface AgeValidator {

            public int min();

            public int max();

    }

    /*==========================================================================*/

    class Test{

    private int age;

    @AgeValidator(min=18, max=35)

            public void setAge(int age) {

                    this.age = age;

    }

    }

    /*==========================================================================*/

    import java.lang.annotation.Annotation;

    import java.lang.reflect.Method;

    String className="com.classTest.Person";

                    Class clazz = Class.forName(className);

                    Object obj = clazz.newInstance();

                    Method method = clazz.getDeclaredMethod("setAge", int.class);

                    int val =22;

                    Annotation annotation = method.getAnnotation(AgeValidator.class);

                   

                    if(annotation != null) {

                           if(annotation instanceof AgeValidator) {

                                   AgeValidator ageValidator = (AgeValidator)annotation;

                                    if (val < ageValidator.min() || val > ageValidator.max()) {

                                            throw new RuntimeException("非法年龄");

                                    }

                           }

                    }

    //                    method.invoke(obj, 20);

                           System.out.println(obj);

    10.8 反射_泛型

    方法

    描述

    Type getGenericSuperclass()

    获取父类泛型类型

    getActualTypeArguments()

    获取实际的泛型类型参数数组

    【Example1】

    import java.lang.reflect.ParameterizedType;

    import java.lang.reflect.Type;

    import java.util.Arrays;

    public class Dao <T>{

            private Class<T> clazz;

            public  Dao() {

                    System.out.println("Dao Constructor ...");

                    System.out.println(this);

                    System.out.println(this.getClass());

    // 获取带泛型参数的父类

                    Type type = this.getClass().getGenericSuperclass();

                    System.out.println(type);

    // 获取具体的泛型参数

                    if(type instanceof ParameterizedType) {

                            ParameterizedType parameterizedType = (ParameterizedType)type;

                           Type [] args = parameterizedType.getActualTypeArguments();

                           System.out.println(Arrays.asList(args));

                           if(args != null && args.length > 0 ) {

                                   Type arg = args[0];

                                   if(arg instanceof Class) {

                                           clazz = (Class<T>) arg;

                                   }

                           }

                     }

            }

    【Example2】

    public class BaseDao <T , PK> {}

    public class Employee {}

    public class EmployeeDao extends BaseDao<Employee, String> {}

    @SuppressWarnings("unchecked")

    public static Class getSuperClassGenricType(Class clazz, int index) {

            Type type = clazz.getGenericSuperclass();

                   

            if(!(type instanceof  ParameterizedType)) {return null;}

            ParameterizedType parameterizedType = (ParameterizedType)type;

            Type [] args = parameterizedType.getActualTypeArguments();

            if(args == null ) {return null;}

            if(index <0 || index > args.length - 1) {return null;}

            Type arg = args[index];

            if(arg instanceof Class) {

                    return (Class) arg;

            }

            return null;

    }

    Class clazz = EmployeeDao.class;

    Class argClazz  = getSuperClassGenricType(clazz, 0 );

    System.out.println(argClazz); // class com.classTest.Employee

    argClazz = getSuperClassGenricType(clazz, 1);

    System.out.println(argClazz); // class java.lang.String

    10.9 创建动态代理

    • Proxy 提供用于创建动态代理类和代理对象的静态方法, 它也是所有动态代理类的父类.
    • Proxy 提供了两个方法来创建动态代理类和动态代理实例
    • 使用动态代理实现 AOP

    AOP(Aspect Orient Program, 面向切面编程)

    动态代理增加的普通方法

    回调目标对象的方法

    动态代理增加的普通方法

    public interface ArithmeticCalculator {

            int add(int i , int j);

            int sub(int i, int j);  

    }

    /*=======================================================================*/

    public class ArithmeticCalculatorImpl2  implements ArithmeticCalculator{     

            @Override

            public int add(int i, int j) {

                    return i+ j;

            }

            @Override

            public int sub(int i, int j) {

                    return i - j;

            }

    }

    /*=======================================================================*/

    关于动态代理的细节:

    1. 需要一个被代理的对象
    2. 类加载器通常是和被代理对象使用相同的类加载器
    3. 一般地,Proxy.newInstance()的返回值是一个被代理对象实现的接口的类型
    4. 当然也可以是其它的接口的类型

    注意:第二个参数,必须是一个接口类型的数组

    提示:若代理对象不需要额外实现被代理对象实现的接口以外的接口,可以使用

    target.getClass().getInterfaces()

    1. InvocationHandler 通常使用匿名内部类的方式:被代理对象需要是final类型的
    2. InvocationiHandler的invoke()方法中的第一个参数Object类型的proxy指的是正在被返回的那个代理对象,一般情况下不使用。

    ArithmeticCalculatorImpl arith = new ArithmeticCalculatorImpl();

        /* ClassLoader:由动态代理产生的对象由哪个类加载器加载,

         * 通常情况下和被代理对象使用一样的类加载器

         * Class<?>[]:由动态代理产生的对象必须需要实现的接口的Class数组

         * InvocationHandler:当具体调用对象的方法时,将产生什么行为

         * */

       final ArithmeticCalculator arithmeticCalculator = new   ArithmeticCalculatorImpl2();

       ArithmeticCalculator proxy =

         (ArithmeticCalculator)Proxy.newProxyInstance(

          arithmeticCalculator.getClass().getClassLoader(),

          new Class[] {ArithmeticCalculator.class},

          new InvocationHandler() {

          @Override

         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

           System.out.println("^_^This Method  ["+method.getName() +"] Begin  ...^_^");

           //调用被代理类的目标方法

           Object result = method.invoke(arithmeticCalculator, args);

           System.out.println("This Method  ["+method.getName()+"] End  ...");

           return result;

         }

    });

    11            多线程

    • 程序(program)是对数据描述与操作的代码的集合,是应用程序执行的脚本。
    • 进程(process)是程序的一次执行过程,是系统运行程序的基本单位。程序是静态的,进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。
    • 多任务(multi task)在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程。
    • 线程(thread):比进程更小的运行单位,是程序中单个顺序的流控制。一个进程中可以包含多个线程。
    • 简单来讲,线程是一个独立的执行流,是进程内部的一个独立执行单元,相当于一个子程序。
    • 一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。
    • 操作系统给每个线程分配不同的CPU时间片,在某一时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行。
    • 每个Java程序启动后,虚拟机将自动创建一个主线程

    11.1 线程

    可以通过以下两种方式自定义线程类:

    –       创建 java.lang.Thread 类的子类,重写该类的 run方 法

    –       创建 java.lang.Runnable接 口的实现类,实现接口中的 run 方法

    • 继承 Thread 类:

    构造方法

    含义

    Thread()

    创建一个新的线程对象

    Thread(Runnable target)

    基于Runnable接口实现类的实例创建一个线程对象

    Thread(Runnable t,String name)

    基于给定的Runnable接口实现类的实例和指定名字创建一个线程对象

    Thread(String name)

    基于给定的名称创建一个线程对象

    • Thread类中的重要方法:

    run方法:包括线程运行时执行的代码,通常在子类中重写它。

    start方法:启动一个新的线程,然后虚拟机调用新线程的run方法

    【Example】

    public class FirstThread extends Thread {

            public FirstThread(String name) {

                    super(name);

            }

            @Override

            public void run() {

                    System.out.println(this.currentThread().getName() + " running ");

            }

            public static void main(String[] args) {

                    FirstThread thread1 = new FirstThread("Thread1"); //创建线程对象

                    FirstThread thread2 = new FirstThread("Thread2"); //创建线程对象

                    thread1.start();       //启动线程

                    thread2.start();       //启动线程

                    for(int i=0;i<30 ; i++) {

                           String threadName = Thread.currentThread().getName();

                           System.out.println("线程"+threadName +i);

                    }

            }

    }

    11.2 Runnable 接口

    • Runnable 接口中只有一个未实现的 run 方法,实现该接口的类必须重写该方法。
    • Runnable 接口与 Thread 类之间的区别

    –       Runnable 接口必须实现 run 方法,而 Thread 类中的run 方法是一个空方法,可以不重写

    –       Runnable 接口的实现类并不是真正的线程类,只是线程运行的目标类。要想以线程的方式执行 run 方法,必须依靠 Thread 类

    –       Runnable 接口适合于资源的共享

    Thread(Runnable target)       :基于Runnable接口实现类的实例创建一个线程对象

    【Example】

    public class MyRunnable implements Runnable {

            private int i=0;

            @Override

            public void run() {

                    for(;i<100; i++) {

                           System.out.println(Thread.currentThread().getName() + ":"+i);

                    }

            }

            public static void main(String[] args) {

                    MyRunnable mr = new MyRunnable();

                    Thread thread1 = new Thread(mr,"thread1");

                    Thread thread2 = new Thread(mr,"thread2");

                    thread1.start();

                    thread2.start();

            }

    }

    11.3 线程的生命周期

    • 线程的生命周期:

    l  指线程从创建到启动,直至运行结束

    l  可以通过调用 Thread 类的相关方法影响线程的运行状态

    • 线程的运行状态

    l  新建(New)

    l  可执行(Runnable)

    l  运行(Running)

    l  阻塞(Blocking)

    l  死亡(Dead)

    1. 新建状态(New)

    当创建了一个Thread对象时,该对象就处于“新建状态”

    没有启动,因此无法运行

    1. 可执行状态(Runnable)

    其他线程调用了处于新建状态线程的start方法,该线程对象将转换到“可执行状态”

    线程拥有获得CPU控制权的机会,处在等待调度阶段

    1. 运行状态(Running)

    处在“可执行状态”的线程对象一旦获得了 CPU 控制权,就会转换到“执行状态”

    在“执行状态”下,线程状态占用 CPU 时间片段,执行run 方法中的代码

    处在“执行状态”下的线程可以调用 yield() 方法,该方法用于主动出让 CPU 控制权。线程对象出让控制权后回到“可执行状态”,重新等待调度。

    public class MyThread extends Thread {

            @Override

            public void run() {

                    for(int i=0;i<100;i++) {

                           System.out.println(this.currentThread().getName() + " " +i);

                           if( i % 10 ==0 ) {

    yield();

    }

                    }}

    }

    1. 阻塞状态(Blocking)

    线程在“执行状态”下由于受某种条件的影响会被迫出让CPU控制权,进入“阻塞状态”。

    1. 进入阻塞状态的三种情况

    n  调用sleep方法

    public void sleep(long millis); Thread.sleep(2000); //2000毫秒

    Thread类的sleep方法用于让当前线程暂时休眠一段时间

    参数 millis 的单位是毫秒

    n  调用 join 方法(合并某个线程)

    处在“执行状态”的线程如果调用了其他线程的 join 方法,

    将被挂起进入“阻塞状态”

    目标线程执行完毕后才会解除阻塞,回到 “可执行状态”

    n  执行I/O操作

    线程在执行过程中如果因为访问外部资源(等待用户键盘输入、访问网络)时发生了阻塞,也会导致当前线程进入“阻塞状态”。

    public class MyThread extends Thread {

            public MyThread(String name) {

                    super(name);

            }

            @Override

            public void run() {

                    for(int i=0;i<100;i++) {

                           System.out.println(this.currentThread().getName() + " " +i);

                    }

            }

            public static void main(String[] args) throws InterruptedException {

                    MyThread thread1 = new MyThread("thread1");

                    thread1.start();

                    for(int i=0;i<100;i++) {

                                   System.out.println(Thread.currentThread().getName() + " " +i);

                                   if (i == 10) {

                                     thread1.join();

                                   }

                           }

                    Thread.sleep(1000); //睡眠1s

                    System.out.println("Bye !");

            }

    }

    1. 解除阻塞

    睡眠状态超时

    调用 join 后等待其他线程执行完毕

    I/O 操作执行完毕

    调用阻塞线程的 interrupt 方法(线程睡眠时,调用该线程的interrupt方法会抛出InterruptedException

    public class InterruptTest extends  Thread{

            @Override

            public void run() {

                    System.out.println("running ...");

                    try {

                           Thread.sleep(10000);

                    } catch (InterruptedException e) {

                           e.printStackTrace();

                    }

            }

            public static void main(String[] args) throws InterruptedException {

                    InterruptTest th1 = new InterruptTest();

                    System.out.println(th1.isAlive());

                    th1.start();

                    System.out.println(th1.isAlive());

                    th1.interrupt();

                    th1.join();

                    System.out.println("End !");

            }

    }

    11.4 *线程的优先级

    • Thread类提供了获取和设置线程优先级的方法

    –      getPriority:获取当前线程的优先级

    –      setPriority:设置当前线程的优先级

    • Java语言为线程类设置了10个优先级,分别使用1~10内的整数表示 ,整数值越大代表优先级越高。每个线程都有一个默认的优先级,主线程的默认优先级是5
    • Thread类定义的三个常量分别代表了几个常用的优先级:

    –      MAX_PRIORITY::代表了最高优先级10

    –      MIN_PRIORITY::代表了最低优先级1

    –      NORM_PRIORITY::代表了正常优先级5

    • setPriority 不一定起作用,在不同的操作系统、不同的 JVM 上,效果也可能不同。操作系统也不能保证设置了优先级的线程就一定会先运行或得到更多的CPU时间。
    • 在实际使用中,不建议使用该方法
    • 多线程应用程序同时访问共享对象时,由于线程间相互抢占CPU的控制权,造成一个线程夹在另一个线程的执行过程中运行,所以可能导致错误的执行结果。
    • 为了防止共享对象在并发访问时出现错误,Java中提供了”synchronized”关键字。
    • synchronized关键字:确保共享对象在同一时刻只能被一个线程访问,这种处理机制称为“线程同步”或“线程互斥”。Java中的“线程同步”基于“对象锁”的概念

    11.5 线程安全

    1.同步方法

    //定义同步方法

     public synchronized void methd(){

           //方法实现

     }

    修饰方法:被“synchronized”关键字修饰的方法称为”同步方法”

    当一个线程访问对象的同步方法时,被访问对象就处于“锁定”状态,访问该方法的其他线程只能等待,对象中的其他同步方法也不能访问,但非同步方法则可以访问.

    2.同步块

    使用 ”synchronized” 关键字:修饰部分代码,如果只希望同步部分代码行,可以使用“同步块”

    //同步块

     synchronized(obj){

           //被同步的代码块

     }

    同步块的作用与同步方法一样,只是控制范围有所区别

    【Example】

    public class Apple  implements Runnable{

            private int Count = 5;

           

            public synchronized void getApple()  {

                    if(Count >0 ) {

                           Count--;

                           try {

                                   Thread.sleep(100);

                           } catch (InterruptedException e) {

                                   e.printStackTrace();

                           }

                           System.out.println(Thread.currentThread().getName()+

                                                   "拿了一个苹果,还剩"+Count+"苹果");

                           }

                    }

           

            @Override

            public void run() {

                    while(Count>0) {

                           getApple();

                    }

                    System.out.println(Thread.currentThread().getName()+"线程结束");

                   

            }

           

            public static void main(String[] args) {

                    Apple apple = new Apple();

                    Thread thread1 = new Thread(apple,"小明");

                    Thread thread2 = new Thread(apple,"小强");

                    thread1.start();

                    thread2.start();

            }

    }

    11.6 线程通信

    • wait()方法:

    –      中断方法的执行,使本线程等待,暂时让出 cpu 的使用权,并允许其他线程使用这个同步方法。

    • notify()方法:

    –      唤醒由于使用这个同步方法而处于等待线程的 某一个结束等待

    • notifyall()方法:

    –      唤醒所有由于使用这个同步方法而处于等待的线程结束等待

    public class TicketsHouse implements Runnable {

            int fiveAmount = 1, tenAmount=0, twentyAmount=0;

            public String getName() {

                    return Thread.currentThread().getName();

            }

            @Override

            public void run() {

                           if(getName().equals("张飞")) {

                                   saleTichet(20);

                           } else if(getName().equals("李逵")) {

                                   saleTichet(5);

                           }else if(getName().equals("刘备")) {

                                   saleTichet(5);

                           }

            }

            private synchronized void saleTichet(int money) {

                    if(money ==5) {

                           fiveAmount = fiveAmount + 1;

                           System.out.println("给" + getName() + "入场券" + getName() + "的钱正好");

                    }else if(money == 20) {

                           while(fiveAmount < 3) {

                                   try {

                                           System.out.println(" " +getName() + "靠边等....");

                                           wait();

                                           System.out.println(" "+getName() + "继续买票");

                                   }catch(InterruptedException e) {

                                          

                                   }              

                           }

                           fiveAmount = fiveAmount -3;

                           twentyAmount = twentyAmount + 1;

                            System.out.println("给"+getName() + "入场券," + getName() + "给20找15元");

                    }

                    notifyAll();

                    //notify();

            }

           

            public static void main(String[] args) {

                    TicketsHouse officer = new TicketsHouse();

                    Thread zhangfei = new Thread(officer,"张飞");

                    Thread likui = new Thread(officer,"李逵");

                    Thread liubei = new Thread(officer,"刘备");

                    zhangfei.start();

                    likui.start();

                    liubei.start();

            }

    }

    12            网络编程

    12.1 网络编程概述

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

    13            设计模式

    https://www.cnblogs.com/pony1223/p/7608955.html

    13.1 什么是设计模式

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

    13.2 设计模式的三个分类

    创建型模式(5种)对象实例化的模式,创建型模式用于解耦对象的实例化过程。工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。

    l  结构型模式(7种):把类或对象结合在一起形成一个更大的结构。适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。

    l  行为型模式(11种):类和对象如何交互,及划分责任和算法。策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    13.3 概说23种设计模式

    13.3.1 单例模式

    单例模式,它的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。

    单例模式具备典型的3个特点:

    1、只有一个实例。

    2、自我实例化。

    3、提供全局访问点。

    因此当系统中只需要一个实例对象或者系统中只允许一个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使用单例模式。

    单例模式的主要优点就是节约系统资源、提高了系统效率,同时也能够严格控制客户对它的访问。也许就是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,所以扩展起来有一定的困难。其UML结构图非常简单,就只有一个类,如下图

    【Example】

    public class Single {

                    // 1.私有化构造器

                    private Single() {

                           System.out.println("single construct");

                    }

                    // 2.因为在类的外部不能创建类的实例,只能在类的内部创建

                    // 3.为了让类的外部可以直接使用该实例,使用static修饰

                    // 4.不能在类的外部可以修改该属性,私有化该属性,同时提供公共的get方法

                    private static Single instance = new Single();

                    public static Single getInstance() {

                           return instance;

    }}

    public class SingleTest {

            public static void main(String[] args) {

                    Single s1 = Single.getInstance();

                    Single s2 = Single.getInstance();

                    System.out.println(s1 == s2 );

    }}

    结果:

    single construct

    true

    14            java打jar包

    https://www.cnblogs.com/mq0036/p/8566427.html

    https://blog.csdn.net/wangpeng047/article/details/7176024

    14.1 MANIFEST.MF编写

    Manifest-Version:  1.0

    Main-Class:  com.hejing.paserTsp.ShowResultFrame

    Class-Path:  lib/commons-beanutils-1.8.0.jar

     lib/byte-buddy-1.7.5.jar

     lib/client-combined-3.8.1.jar

     lib/client-combined-3.8.1-sources.jar

     lib/commons-codec-1.10.jar

     lib/commons-collections-3.2.1.jar

     lib/commons-exec-1.3.jar

     lib/commons-lang-2.5.jar

     lib/commons-logging-1.1.1.jar

    这个文件的编写需要遵循严格的规范:

    1)        第一行不能空,行与行之间不能有空行,每一行的最后一个字符不能是空格。

    2)        最后一行一定是空行。

    3)        每个属性的名称和值之间(冒号后面)一定要有空格。

    4)        文件的每一行都不能超过72个字节(一般是70个ASCII字母加上回车换行符);如果72个字节不够用,则另起一行并以空格开头:以空格开头的行都被视为前一行的续行,不要使用TAB键,否则会报错:invalid header。

    Class-Path属性指定的类或jar包是本地的文件,不可以是远程访问的类或者JAR包文件中的JAR包,即不能是jar in jar,当然也就不能是本jar包中包含的jar包。要实现对jar in jar的引用,需要自定义相关代码来读取它们。上面提到的RunnableJAR file之所以能够运行,就是因为Eclipse为我们提供了jarinjarloader,来帮助我们读取jar in jar。

    14.2 打jar 包

    jar -d . Hello.java

    java com.hello.Hello

    14.2.1 使用Eclipse的Export功能

    一、打包成一般的jar

    步骤如下:

      1)在要打包的项目上右击,选择Export。

      2)在弹出的窗口中,选择Java -> JAR File,然后点击next按钮。

      3)在JAR File Specification窗口中,设置打包成的文件名和存放位置,点击两侧next。

      4)在JAR Manifest Specification窗口中,设置MANIFEST.MF清单文件的配置,若仅仅打包成单纯的jar包的话,不用做任何修改,采取默认即可,若打包成可执行jar包的话,可以使用已存在的MANIFEST文件或者直接选择Main class。

      5)点击Finish按钮,完成打包。

    二、打包成可运行的jar

    步骤如下:

      1)在要打包的项目上右击,选择Export。

      2)在弹出的窗口中,选择Java -> Runnable JAR File,然后点击next按钮。

      3)在Runnable JAR File Specification窗口中,选择Launch configuration和Export destination。

      4)点击Finish按钮,打包完成

    15 问题

    15.1 window打印中文乱码

    编译时:javac -encoding utf-8 HelloWorld.java

  • 相关阅读:
    chrome 插件备份
    github下载单个文件
    idea插件备份
    外卖类应用的竞争与趋势
    使用终端和Java API对hbase进行增删改查操作
    分布式文件系统的布局、文件查找
    Java上机实验报告(4)
    Java上机实验报告(3)
    Java上机实验报告(2)
    Java上机实验报告(1)
  • 原文地址:https://www.cnblogs.com/boye169/p/13394549.html
Copyright © 2011-2022 走看看