zoukankan      html  css  js  c++  java
  • JAVA第二阶段

    一、类变量和类方法

     1、类变量

    类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

    类变量定义方式

    访问修饰符 static 数据类型 变量名;  【推荐】
    static 访问修饰符 数据类型 变量名;

    类变量内存布局

    package com.study;
    public class run {
        public static void main(String[] args) {
            money money1 = new money();
            money money2 = new money();
            money1.payfee(50);
            money1.payfee(100);
            System.out.println(money.getFee());
        }
    }
    class money{
        private int salary = 5000;
        private static double fee = 0;
        public void payfee(double fee) {
            money.fee += fee;
        }
        public static double getFee(){
            return fee;
        }
    }

    类变量访问方式

    类名.类变量名
    //或者
    对象名.类变量名 //静态变量访问修饰符的访问权限和范围与普通属性是一样的
    //推荐使用:类名.类变量名;

    类变量使用注意事项和细节讨论

    1> 什么时候需要用类变量

    当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)

    2> 类变量与实例变量(普通属性)区别

    类变量是该类的所有对象共享的,而实例变量是每个对象独享的

    3> 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

    4> 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐我们使用类名.类变量名方式访问【前提是满足访问修饰符的访问权限和范围】

    5> 实例变量不能通过类名.类变量名方式访问

    6> 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了

    7> 类变量的生命周期是随类的加载开始,随着类消亡而销毁

    2、类方法

    类方法也叫静态方法,形式如下:

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

    类方法的调用

    类名.类方法名
    //或者
    对象名.类方法名  //前提是满足访问修饰符的访问权限和范围

     类方法应用案例

    package com.study;
    public class run {
        public static void main(String[] args) {
            money.payfee(50);
            money.payfee(100);
            System.out.println(money.getFee());  //输出结果:150
        }
    }
    class money{
        private int salary = 5000;
        private static double fee = 0;
        public static void payfee(double fee) {
            money.fee += fee;
        }
        public static double getFee(){
            return fee;
        }
    }

    类方法经典的使用场景

    当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率

    比如:工具类中的方法utils、Math类、Arrays类、Collections 集合类

    在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如:打印一维数组,冒泡排序,完成某个计算任务等

    package com.study;
    public class run {
        public static void main(String[] args) {
            //调用方法都没有创建对象
            myTools.getSum(10,50);
            myTools.getAbs(-50);
        }
    }
    class myTools{
        public static void getSum(int n1 , int n2){
            System.out.println(n1 + n2);
        }
        public static void getAbs(int n1){
            System.out.println(Math.abs(n1));  //没有创建Math对象即可调用abs方法,因为abs方位是static
        }
    }

    类方法使用注意事项和细节讨论

    1> 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区∶

      类方法中无this的参数

      普通方法中隐含着this的参数

    2> 类方法可以通过类名调用,也可以通过对象名调用

    package com.study;
    public class run {
        public static void main(String[] args) {
            myTools.getSum(10,50);  //通过类名调用
            myTools myTools1 = new myTools();
            myTools1.getSum(10,20);  //通过对象调用
        }
    }
    class myTools{
        public static void getSum(int n1 , int n2){
            System.out.println(n1 + n2);
        }
        public static void getAbs(int n1){
            System.out.println(Math.abs(n1));
        }
    }

    3> 普通方法和对象有关,需要通过对象名调用,比如 对象名.方法名(参数),不能通过类名调用

    4> 类方法中不允许使用和对象有关的关键字,比如this和super;普通方法(成员方法)可以

    class myTools{
        private int n1 = 0;
        private static int n2 = 10;
        public static void setN(int n){
            this.n1 = n;  //这样是错误的,类方法中不允许使用和对象有关的关键字
        }
        public void setM(int m){
            this.n1 = m;  //普通方法(成员方法)允许使用和对象有关的关键字
        }
    }

    5> 类方法(静态方法)中只能访问静态变量或静态方法;普通成员方法,既可以访问非静态成员,也可以访问静态成员

    class myTools{
        private int n1 = 0;
        private static int n2 = 10;
        public static void f1(){
            System.out.println(n1);  //这样是错误的,静态方法中不允许访问非静态成员
            System.out.println(n2);  //静态方法中只能访问静态变量或静态方法
        }
    }

    小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)

    3、课堂练习

    package com.study;
    public class run{
        static int count = 9;
        public void count(){
            System.out.println("count=" +(count++));
        }
        public static void main(String args[]) {
            new run().count();    //count=9
            new run().count();    //count=10
            System.out.println(run.count);    //11
        }
    }

    二、理解main方法

    1、main方法是java虚拟机调用

    2、java虚拟机需要调用类的main()方法时,和main()方法不在同一个类中,所以main()方法的访问权限必须是public

    3、java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static

    4、该方法接收String类型的数组参数,该数组中保存运行java命令时传递给所运行类的参数

    特别提示

    1、在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性

    2、在main()方法中,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员

    package com.study;
    public class run{
        private static int num1 = 100;  //定义静态属性
        private int num2 = 200;         //定义非静态属性
        public static void f1(){        //定义静态方法
            System.out.println("static的f1方法");
        }
        public void f2(){               //定义非静态方法
            System.out.println("f2方法");
        }
        public static void main(String[] args){
            //以下调用是正确的
            System.out.println(num1);
            f1();
            //以下调用是错误的
            System.out.println(num2);
            f2();
            //需要创建对象之后才可以访问非静态成员
            run run = new run();
            System.out.println(run.num2);
            run.f2();
        }
    }

    三、代码块

    1、代码块概述

    代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{ }包围起来

    但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用

    [修饰符]{
            代码块
        };

    说明注意

    1> 修饰符可选,要写的话,也只能写static

    2> 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的叫普通代码块/非静态代码块

    3> 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)

    4> " ; "号可以写上,也可以省略。

    代码块的好处

    1> 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作

    2> 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性

    //未使用代码块
    package com.study;
    public class block{
        private String name;
        private int age;
        private double salary;
        //构造器
        public block(String name) {
            System.out.println("程序开始执行...");
            System.out.println("程序执行50%...");
            System.out.println("程序执行完毕...");
            this.name = name;
        }
        public block(String name, int age) {
            System.out.println("程序开始执行...");
            System.out.println("程序执行50%...");
            System.out.println("程序执行完毕...");
            this.name = name;
            this.age = age;
        }
        public block(String name, int age, double salary) {
            System.out.println("程序开始执行...");
            System.out.println("程序执行50%...");
            System.out.println("程序执行完毕...");
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
    }
    //使用代码块
    package com.study;
    public class block1 {
        private String name;
        private int age;
        private double salary;
        //代码块
        {
            System.out.println("程序开始执行...");
            System.out.println("程序执行50%...");
            System.out.println("程序执行完毕...");
        }
        //构造器
        public block1(String name) {
            this.name = name;
            System.out.println("block1(String name)被调用");
        }
    
        public block1(String name, int age) {
            this.name = name;
            this.age = age;
            System.out.println("block1(String name, int age)被调用");
        }
    
        public block1(String name, int age, double salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
            System.out.println("block1(String name, int age, double salary)被调用");
        }
    }

    不管我们调用哪个构造器,创建对象,都会先调用代码块的内容,代码块调用的顺序优先于构造器

    2、代码块使用注意事项和细节讨论

    1> static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次;如果是普通代码块,每创建一个对象,就执行一次

    package com.study;
    public class block{
        public static void main(String[] args) {
            //普通代码块,每创建一个对象,就执行一次
            AA aa1 = new AA();
            AA aa2 = new AA();
            //输出结果:
            //AA类的普通代码块被执行
            //AA类的普通代码块被执行
    
            //static代码块随着类的加载而执行,并且只会执行一次
            BB bb1 = new BB();
            BB bb2 = new BB();
            //输出结果:
            //BB类的static代码块被执行
        }
    }
    class AA{
        {
            System.out.println("AA类的普通代码块被执行");
        }
    }
    class BB{
        static {
            System.out.println("BB类的static代码块被执行");
        }
    }

    2> 类什么时候被加载[重要背!]

    ① 创建对象实例时(new)

    ② 创建子类对象实例,父类也会被加载

    ③ 使用类的静态成员时(静态属性,静态方法)

    package com.study;
    public class block{
        public static void main(String[] args) {
            //①创建对象实例时(new)
            AA aa = new AA();
            //输出结果:AA类的普通代码块被执行
    
            //②创建子类对象实例,父类也会被加载
            BB bb = new BB();
            //输出结果:
            //AA类的普通代码块被执行
            //BB类的普通代码块被执行
    
            //③使用类的静态成员时(静态属性,静态方法)
            //如果是只使用类的静态成员,普通代码块并不会执行,static代码块在类加载时会执行一次
            System.out.println(CC.salary);
            //输出结果:
            //CC类的static代码块被执行
            //5000
        }
    }
    class AA{
        {
            System.out.println("AA类的普通代码块被执行");
        }
    }
    class BB extends AA{  //BB类继承AA类
        {
            System.out.println("BB类的普通代码块被执行");
        }
    }
    class CC{
        public static int salary = 5000;
        static {
            System.out.println("CC类的static代码块被执行");
        }
    }

    3> 普通的代码块,在创建对象实例时,会被隐式的调用,对象被创建一次,普通的代码块就会调用一次

    如果只使用类的静态成员,普通代码块并不会执行。

    package com.study;
    public class block{
        public static void main(String[] args) {
            System.out.println(AA.salary);
            //输出结果:
            //AA类的static代码块被执行
            //5000
        }
    }
    class AA{
        public static int salary = 5000;
        {
            System.out.println("AA类的普通代码块被执行");
        }
        static {
            System.out.println("AA类的static代码块被执行");
        }
    }

    小结:

    ① static代码块是类加载时执行,且只会执行一次

    ② 普通代码块是在创建对象时调用的,创建一次调用一次

    ③ 类加载的3种情况,需要记住

    4> 创建一个对象时,在一个类里调用顺序(重点,难点)

    ① 调用静态代码块和静态属性初始化 (注意:静态代码块和静态属性初始化调用的优先级一样,如果存在多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)

    ② 调用普通代码块和普通属性的初始化 (注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)

    ③ 调用构造方法

    package com.study;
    public class block{
        public static void main(String[] args) {
            AA aa = new AA();
        }
    }
    class AA{
        public static int salary = getSalary();
        public int height = getHeight();
        {
            System.out.println("AA类的普通代码块被执行");
        }
        static {
            System.out.println("AA类的static代码块被执行");
        }
        public int getHeight(){
            System.out.println("getHeight()方法被调用");
            return 180;
        }
        public static int getSalary(){
            System.out.println("getSalary()方法被调用");
            return 10000;
        }
        AA(){
            System.out.println("AA()的无参构造器被调用");
        }
    }
    //打印结果:
    //getSalary()方法被调用
    //AA类的static代码块被执行
    //getHeight()方法被调用
    //AA类的普通代码块被执行
    //AA()的无参构造器被调用

    5> 构造器的最前面其实隐含了super()和调用普通代码块;静态相关的代码块和属性初始化在类加载时,就执行完毕了,因此是优先于构造器和普通代码块执行的

    package com.study;
    public class block{
        public static void main(String[] args) {
            BB bb = new BB();
        }
    }
    class AA{
        {//普通代码块
            System.out.println("AA类的普通代码块");
    
        }
        AA(){
            //隐藏代码1、super();
            //隐藏代码2、调用本类普通代码块
            System.out.println("AA()无参构造器被调用");
        }
    }
    class BB extends AA{
        {  //普通代码块
            System.out.println("BB类的普通代码块");
        }
        BB(){
            //隐藏代码1、super();
            //隐藏代码2、调用本类普通代码块
            System.out.println("BB()无参构造器被调用");
        }
    }
    //打印结果:
    //AA类的普通代码块
    //AA()无参构造器被调用
    //BB类的普通代码块
    //BB()无参构造器被调用

    6> 创建一个子类时(存在继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:

    ① 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)

    ② 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)

    ③ 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

    ④ 父类的构造方法

    ⑤ 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

    ⑥ 子类的构造方法

    public class block {
        public static void main(String[] args) {
            BB bb = new BB();
        }
    }
    class AA{
        {//AA类的普通代码块
            System.out.println("AA类的普通代码块");
        }
        static {//AA类的static代码块
            System.out.println("AA类的static代码块");
        }
        private static int age = getAge();
        private int height = getHeight();
    
        public AA() {
            //隐藏1:super();
            //隐藏2:普通代码块和普通属性初始化
            System.out.println("AA的无参构造器被调用");;
        }
        public static int getAge(){
            System.out.println("static int getAge()被调用");
            return 10;
        }
        public int getHeight(){
            System.out.println("int getHeight()被调用");
            return 175;
        }
    }
    class BB extends AA{
        {//BB类的普通代码块
            System.out.println("BB类的普通代码块");
        }
        static {//BB类的static代码块
            System.out.println("BB类的static代码块");
        }
        private static String name  = getName();
        private int weight = getWeight();
    
        public BB() {
            //隐藏1:super();
            //隐藏2:普通代码块和普通属性初始化
            System.out.println("BB的无参构造器被调用");;
        }
        public static String getName(){
            System.out.println("static String getName()被调用");
            return "King";
        }
        public  int getWeight(){
            System.out.println("int getWeight()被调用");
            return 140;
        }
    }
    //打印结果:
    //AA类的static代码块
    //static int getAge()被调用
    //BB类的static代码块
    //static String getName()被调用
    //A类的普通代码块
    //int getHeight()被调用
    //AA的无参构造器被调用
    //BB类的普通代码块
    //int getWeight()被调用
    //BB的无参构造器被调用

    ① 静态属性初始化和静态代码块是在类加载的时候就会运行的,在创建BB类对象的时候就会要加载BB类,但是BB类继承了AA类,所以会先加载AA类,所以AA类的静态方法和静态代码块就会执行

    ② AA类加载完成之后才会加载BB类,然后BB类的静态方法和静态代码块就会执行,执行完毕之后就会走BB类的构造器,但是BB类的构造器隐藏了super();和普通代码块

    ③ 所以就会先执行AA类的构造器,所以会先运行AA类的构造器,AA类的构造器也隐藏了代码,所以先会执行AA类的普通代码块和普通属性初始化,执行完毕之后执行AA类的构造器

    ④ 执行完毕之后再返回到BB类的构造器继续执行BB类隐藏的普通代码块和普通属性初始化,最后再执行BB类的构造器

    7> 静态代码块只能直接调用静态成员 (静态属性和静态方法),普通代码块可以调用任意成员

    class AA{
        private static int age = 100;
        private int height = 175;
        private static void f1(){
            System.out.println("static void f1()");
        }
        private void f2(){
            System.out.println("void f2()");
        }
        static { //静态代码块
            System.out.println(age);
            System.out.println(height);  //会报错,用不了
            f1();
            f2();   //会报错,用不了
        }
        { //普通代码块
            System.out.println(age);
            System.out.println(height);
            f1();
            f2(); 
        }
    }

    四、单例模式

    单例(单个的实例),是静态方法和属性的经典使用

    1> 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

    2> 单例模式有两种方式:① 饿汉式    ② 懒汉式

    饿汉式创建步骤:

    1> 将构造器私有化

    2> 在类的内部直接创建对象(该对象是static)

    3> 提供一个公共的static方法,返回第二步创建的对象

    懒汉式创建步骤:

    1> 构造器私有化

    2> 定义一个 static静态属性的对象

    3> 提供一个public的static方法,可以返回一个Cat对象

    4> 懒汉式,只当用户使用getCat的时候才会返回Cat对象,后面再次调用返回的还是第一次创建的cat

    public class Singleton {
        public static void main(String[] args) {
            //饿汉式
            GirlFriend gf1 = GirlFriend.getGf();
            System.out.println(gf1);
            GirlFriend gf2 = GirlFriend.getGf();
            System.out.println(gf2);
            System.out.println(gf1 == gf2);
            System.out.println(GirlFriend.age);
            //懒汉式
            System.out.println(Cat.age);
            Cat cat = Cat.getCat();
            System.out.println(cat);
        }
    }
    //饿汉式
    class GirlFriend{
        public static int age = 22;
        private String name;
        //1、将构造器私有化,防止在类外面直接new对象
        private GirlFriend(String name) {
            System.out.println("构造器被调用.....");
            this.name = name;
        }
        //2、在类的内部创建对象(对象需要为static的)
        private static GirlFriend gf = new GirlFriend("任小可");  //只要类加载了,对象就会被创建
        //3、提供一个public的static方法,用于返回gf对象
        public static GirlFriend getGf(){
            return gf;
        }
        //重写toString方法
        @Override
        public String toString() {
            return "GirlFriend{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    //懒汉式
    class Cat{
        public static int age = 2;
        private String name;
        
        //1、将构造器私有化,防止在类外面直接new对象
        private Cat(String name) {
            System.out.println("构造器被调用.....");
            this.name = name;
        }
        //2、定义一个static对象
        private static Cat cat;
        //3、提供公共的静态方法用于返回一个对象
        //4.懒汉式,只当用户使用getCat的时候才会返回Cat对象,后面再次调用返回的还是第一次创建的cat
        public static Cat getCat(){
            if(cat == null){
                cat = new Cat("小可");
            }
            return cat;
        }
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    1> 二者最主要的区别在于创建对象的时机不同:

      饿汉式是在类加载就创建了对象实例

      懒汉式是在使用时才创建

    2> 饿汉式不存在线程安全问题,懒汉式存在线程安全问题

    3> 饿汉式存在浪费资源的可能,因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题

    4> 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式

    五、final关键字

    final 中文意思:最后的、最终的

    final 可以修饰类、属性、方法和局部变量

    在某些情况下,程序员可能有以下需求,就会使用到final

    1、当不希望类被继承时,可以用final修饰

    2、当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰

    3、当不希望类的某个属性值被修改,可以用final修饰

    4、当不希望某个局部变量被修改,可以使用final修饰

    final使用注意事项和细节讨论

    1、final修饰的属性又叫常量,一般用XX_XX XX来命名

    2、final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:

    ①定义时

    ②在构造器中

    ③在代码块中

    class AA{
        //1、定义时
        public final int age1 = 15;
        //2、在构造器中
        public final int age2;
        public AA() {
            age2 = 10;
        }
        //3、在代码块中
        public final int age3;
        {
            age3 = 10;
        }
    }

    3、如果final修饰的属性是静态的,则初始化的位置只能是

    ①定义时

    ②在静态代码块

    不能在构造器中赋值

    4、final类不能继承,但是可以实例化对象

    5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承

    6、一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法

    7、final不能修饰构造方法(即构造器)

    8、final和 static 往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理

    public class final_ {
        public static void main(String[] args) {
            System.out.println(AA.age);
        }
    }
    class AA{
        public final static int age = 15;
        static {
            System.out.println("static代码块被加载");
        }
    }
    //打印结果:15
    //只有 15 ,不会打印 static代码块被加载

    9、包装类(Integer、Double、Float、Boolean、String)等都是final类

    六、抽象类

    1、抽象类介绍

    当父类的一些方法不能确定但需要声明时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类

    abstract class Animals{
        private String name;
        private int age;
        //抽象方法就是没有实现的方法,及没有方法体
        //一般来说抽象类都会被继承,在子类中实现抽象方法
        public abstract void f1();
    }

    1> 用abstract关键字来修饰一个类时,这个类就叫抽象类

    2> 用abstract关键字来修饰一个方法时,这个方法就是抽象方法

    3> 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类

    4> 抽象类是考官比较爱问的知识点,在框架和设计模式使用较多

    2、抽象类使用的注意事项和细节讨论

    1> 抽象类不能被实例化

    2> 抽象类不一定要包含abstract方法。也就是说:抽象类可以没有abstract方法

    3> 一旦类包含了abstract方法,则这个类必须声明为abstract

    4> abstract只能修饰类和方法,不能修饰属性和其它的

    5> 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等

    abstract class Animals{
        private int age;
        private static int height = 100;
        {
            System.out.println("方法体");
        }
        static {
            System.out.println("static方法体");
        }
        public abstract void f1();
        public void f2(){
            System.out.println("f2()方法");
        }
        public static void f3(){
            System.out.println("static void f3()方法");
        }
    }

    6> 抽象方法不能有主体,即不能实现(没有方法体就是没有实现方法;有了方法体即使方法体中没有内容也看作实现了方法)

    7> 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类

    abstract class Animals{
        private int age;
        private static int height = 100;
        public abstract void f1();
        public void f2(){
            System.out.println("f2()方法");
        }
        public abstract void f3();
    }
    //1、自己声明abstract类
    abstract class Cat extends Animals{
    
    }
    //2、实现抽象类的所有抽象方法
    class Dog extends Animals{
        @Override
        public void f1() {
            //有了方法体即使方法体中没有内容也看作实现了方法
        }
        @Override
        public void f3() {
            //有了方法体即使方法体中没有内容也看作实现了方法
        }
    }

    8> 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的

    3、抽象类课堂练习

    编写一个Employee类,声明为抽象类,包含如下三个属性:name、id、salary 提供必要的构造器和抽象方法:work()

    对于Manager类来说,他既是员工,还具有奖金(bonus)的属性

    请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问,实现work(),提示"经理/普通员工名字工作中...”

    请使用OOP的继承+抽象类完成

    public class abstract_ {
        public static void main(String[] args) {
            Mananger king = new Mananger("King", 1001, 8000, 1200);
            CommontEmployee rxk = new CommontEmployee("Rxk", 1002, 5000);
            king.work();
            rxk.work();
        }
    }
    abstract class Employee{
        private String name;
        private int id;
        private double salary;
        //构造器
        public Employee(String name, int id, double salary) {
            this.name = name;
            this.id = id;
            this.salary = salary;
        }
        //声明抽象方法work
        abstract public void work();
        //get、set方法
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public double getSalary() {
            return salary;
        }
        public void setSalary(double salary) {
            this.salary = salary;
        }
    }
    class Mananger extends Employee{
        private double bouns;
        public Mananger(String name, int id, double salary, double bouns) {
            super(name, id, salary);
            this.bouns = bouns;
        }
        @Override
        public void work(){
            System.out.println("经理" + getName() + "正在工作中");
        }
    }
    class CommontEmployee extends Employee{
        public CommontEmployee(String name, int id, double salary) {
            super(name, id, salary);
        }
        @Override
        public void work() {
            System.out.println("员工" + getName() + "正在工作中...");
        }
    }

    4、抽象类模板设计模式

    抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式

    模板设计模式能解决的问题

    1、当功能内部一部分实现是确定,一部分实现是不确定的;这时可以把不确定的部分暴露出去,让子类去实现

    2、编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式.

    public class abstract_ {
        public static void main(String[] args) {
            Calculate1 calculate1 = new Calculate1();
            calculate1.calTime();
            Calculate2 calculate2 = new Calculate2();
            calculate2.calTime();
        }
    }
    //有多个类完成不同的任务job,统计各自任务完成的时间
    //设计抽象类
    abstract class Template{
        public abstract void job();  //定义抽象方法
        public void calTime(){
            long start = System.currentTimeMillis();
            job();  //子类在调用的时候会用到多态绑定机制
            long end = System.currentTimeMillis();
            System.out.println("任务耗时:" + (end - start) + "毫秒");
        }
    }
    //编写Calculate1类
    class Calculate1 extends Template{
        @Override
        public void job() {  //实现父类的job抽象方法
            int sum = 0;
            for (int i = 1; i <= 800000; i++) {
                sum += i;
            }
        }
    }
    //编写Calculate2类
    class Calculate2 extends Template{
        @Override
        public void job() { //实现父类的job抽象方法
            int amass = 1;
            for (int i = 1; i <= 800000; i++) {
                amass *= i;
            }
        }
    }

    上述代码中计算程序运行时间的代码是确定的,各个子类中具体job的代码是不一样的(不确定的),所以把不确定的部分暴露出去设为抽象方法,让子类去实现此方法

    上述代码中父类提供的通用方法就是计算程序运行时间的代码,然后把job的方法交给了子类

    七、接口

    1、接口概述

    接口就是给出一些没有实现的方法,封装到一起,在某个类要使用的时候,再根据具体情况把这些方法写出来

    接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体【jdk7.0】

    接口体现了程序设计的多态和高内聚低偶合的设计思想

    【jdk8.0】后接口类可以有静态方法、默认方法,也就是说接口中可以有方法的具体实现

    //定义接口
    package com.study;
    public interface DateBase {
        public void connect();
        public void close();
    }
    //定义类
    package com.study;
    public class Mysql_ implements DateBase{
        @Override
        public void connect() {
            System.out.println("Mysql数据库已连接...");
        }
        @Override
        public void close() {
            System.out.println("Mysql数据库已关闭...");
        }
    }
    //定义类
    package com.study;
    public class Orance_ implements DateBase{
        @Override
        public void connect() {
            System.out.println("Orance数据库已连接...");
        }
        @Override
        public void close() {
            System.out.println("Orance数据库已关闭...");
        }
    }
    //主方法测试
    package com.study;
    public class interface_ {
        public static void main(String[] args) {
            Mysql_ mysql = new Mysql_();
            database(mysql);
            Orance_ orance = new Orance_();
            database(orance);
        }
        public static void database(DateBase db){
            db.connect();
            db.close();
        }
    }

    2、注意事项和细节

    1> 接口不能被实例化

    2> 接口中所有的方法是 public方法。接口中抽象方法,可以不用abstract修饰

    interface Ainter{
        void aaa();  //实际上是public abstract void aa();
    }

    3> 一个普通类实现接口,就必须将该接口的所有方法都实现

    interface Ainter{
        void aaa();
        void bbb();
    }
    class testclass implements Ainter{
        @Override
        public void aaa() {
            
        }
        @Override
        public void bbb() {
    
        }
    }

    4> 抽象类实现接口,可以不用实现接口的方法

    5> 一个类同时可以实现多个接口

    interface Ainter{
        void AAA();
    }
    interface Binter{
        void BBB();
    }
    class testclass implements Ainter,Binter{
        @Override
        public void AAA() {
        }
        @Override
        public void BBB() {
        }
    }

    6> 接口中的属性,只能是final的,而且是 public static final的修饰符

    interface Ainter{
        int a = 1;  //实际上是 public static final int a=1;(必须初始化)
    }

    7> 接口中属性的访问形式:接口名.属性名

    8> 接口不能继承其它的类,但是可以继承多个别的接口

    interface Ainter{
        void AAA();
    }
    interface Binter{
        void BBB();
    }
    interface Cinter extends Ainter,Binter{
        void CCC();
    }
    class testclass implements Cinter{
        //需要实现所有接口的方法,包括Cinter继承的接口
        @Override
        public void AAA() {
        }
        @Override
        public void BBB() {
        }
        @Override
        public void CCC() {
        }
    }

    9> 接口的修饰符只能是 public 和 默认,这点和类的修饰符是一样的

    3、实现接口 VS 继承类

    当子类继承了父类,就自动的拥有父类的功能

    如果子类需要扩展功能,可以通过实现接口的方式扩展

    可以理解实现接口是对java单继承机制的一种补充.

    public class interface_ {
        public static void main(String[] args) {
            littleMonkey wuKong = new littleMonkey("wuKong");
            wuKong.climb();
            wuKong.swimming();
            wuKong.fly();
        }
    }
    class Monkey{
        private String name;
        public Monkey(String name) {
            this.name = name;
        }
        public void climb(){
            //悟空只要继承了这个类,那么这个类的方法悟空自然就可以使用
            System.out.println(name + "在爬树...");
        }
        public String getName() {
            return name;
        }
    }
    interface Fish{
        void swimming();
    }
    interface Bird{
        void fly();
    }
    class littleMonkey extends Monkey implements Fish,Bird{
        public littleMonkey(String name) {
            super(name);
        }
        @Override
        public void swimming() {
            //假如悟空想要游泳,但是悟空刚开始没有游泳这个能力,java单继承机制又不可能去继承鱼类
            //所以就提供鱼接口,让悟空去实现这个鱼接口方法
            System.out.println(getName() + "通过学习会游泳了...");
        }
        @Override
        public void fly() {
            //假如悟空想要飞翔,但是悟空刚开始没有飞翔这个能力,java单继承机制又不可能去继承鸟类
            //所以就提供鸟接口,让悟空去实现这个鸟接口方法
            System.out.println(getName() + "通过学习会飞翔了...");
        }
    }

    接口和继承解决的问题不同

    继承的价值主要在于:解决代码的复用性和可维护性

    接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法,即更加的灵活

    接口比继承更加灵活,继承是满足is - a的关系,而接口只需满足 like - a的关系

    猴子 is a 动物,猴子 like(像) a 鸟在飞

    接口在一定程度上实现代码解耦 [即:接口规范性+动态绑定机制]

    4、接口的多态性

     1> 多态参数

    public class interface_ {
        public static void main(String[] args) {
            //接口多态的体现
            //接口类型的变量 fish1 可以指向 实现了Fish接口的类的对象实例
            Fish fish1 = new Monkey();
            fish1 = new Cat();
            //继承多态的体现
            //父类类型的变量 aa 可以指向 继承了父类AA的子类的对象实例
            AA aa = new BB();
            aa = new CC();
        }
    }
    //接口多态
    interface Fish{}
    class Monkey implements Fish{}
    class Cat implements Fish{}
    //继承多态
    class AA{}
    class BB extends AA{}
    class CC extends AA{}

    2> 多态数组

    public class interface_ {
        public static void main(String[] args) {
            //*************多态数组的体现***************
            Fish[] fishs = new Fish[2];
            fishs[0] = new Monkey();
            fishs[1] = new Cat();
            for (int i = 0; i < fishs.length; i++) {
                fishs[i].swimming();
                if (fishs[i] instanceof Cat){
                    ((Cat) fishs[i]).eat();
                }
            }
            //****************************************
        }
    }
    //接口多态
    interface Fish{
        void swimming();
    }
    class Monkey implements Fish{
        @Override
        public void swimming() {
            System.out.println("Monkey的swimming()");
        }
    }
    class Cat implements Fish{
        @Override
        public void swimming() {
            System.out.println("Cat的swimming()");
        }
        public void eat(){
            System.out.println("Cat的eat()");
        }
    }

    3> 多态传递

    ① 接口类型的变量可以指向实现接口类的父类的子类的实例

    public class interface_ {
        public static void main(String[] args) {
            //*************多态传递的体现***************
            Monkey littleMonkey = new littleMonkey();
            Fish fish1 = littleMonkey;
            Fish fish2 = new littleMonkey();  //接口类型的变量fish2可以指向实现了Fish类的Monkey类的子类的实例
            Fish fish3 = new Monkey();
            //****************************************
        }
    }
    //多态传递
    interface Fish{
        void swimming();
    }
    class Monkey implements Fish{
        @Override
        public void swimming() {
            System.out.println("Monkey的swimming()");
        }
    }
    class littleMonkey extends Monkey{
    
    }

    ② 子接口继承了父接口,类实现了子接口,相当于类也实现了父接口

    public class interface_ {
        public static void main(String[] args) {
            //*************多态传递的体现***************
            Cat cat = new Monkey(); 
            // Fish接口继承了Cat接口,Monkey类实现了Cat接口,相当于Monkey类也实现了Cat接口
            //****************************************
        }
    }
    //多态传递
    interface Cat{ }
    interface Fish extends Cat{ }
    class Monkey implements Fish{ }

    5、接口练习

    package com.study;
    public class interface_ {
        public static void main(String[] args) {
            Testclass testclass = new Testclass();
            System.out.println(testclass.a);
            System.out.println(Testclass.a);  
            //Testclass类实现了Ainter接口,那么Aniter接口的属肯定能用
            System.out.println(Ainter.a);
            //输出结构都是23,没有任何错误
        }
    }
    interface Ainter{
        int a = 23;  //等价于 public static final int a = 23;
    }
    class Testclass implements Ainter{
    
    }
    interface A{
        int x = 1;
    }
    class B{
        int x = 2;
    }
    class C extends B implements A{
        public void returnX(){
            //System.out.println(x); 错误的,原因是有两个X,不知道指向哪个X
            //访问接口x
            System.out.println(A.x);
            //访问A类的x
            System.out.println(super.x);
        }
        public static void main(String[] args){
            new C().returnX();
        }
    }

    八、内部类

    1、内部类介绍与分类

    一个类的内部又完整的嵌套了另一个类结构

    被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)

    外部类是我们类的第五大成员【思考:类的五大成员是哪些?属性、方法、构造器、代码块、内部类】

    内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

    注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类

    class Outer{//外部类
        //属性
        private String name;
        //方法
        public void f1(){
            
        }
        //构造器
        public Outer(String name) {
            this.name = name;
        }
        //代码块
        {
            
        }
        //内部类
        class inner{
    
        }
    }
    class Other{ //其他类
    
    }

    内部类的分类

    1> 定义在外部类局部位置上(比如方法内、代码块内):

    ① 局部内部类(有类名)

    ② 匿名内部类(没有类名,需要重点掌握!)

    2> 定义在外部类的成员位置上

    ① 成员内部类(没用static修饰)

    ② 静态内部类(使用static修饰)

    2、局部内部类的使用

    局部内部类是定义在外部类的局部位置,比如方法中、代码块中,并且有类名

    1> 可以直接访问外部类的所有成员,包括私有的

    class Outer{//外部类
        //属性
        private String name = "King";
        //方法
        public void f1(){
            class inner{//局部内部类
                public void f2(){
                    System.out.println(name);// 可以直接访问外部类的所有成员,包括私有的
                }
            }
        }
    }

    2> 不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final

    3> 作用域:仅仅在定义它的方法或代码块中

    4> 局部内部类访问外部类的成员的访问方式:直接访问

    5> 外部类访问局部内部类成员的访问方式:创建对象再访问(注意:必须在作用域内)

    public class InnerClass {
        public static void main(String[] args) {
            Outer outer = new Outer();
            outer.f1();
        }
    }
    class Outer{//外部类
        //属性
        private String name = "King";
        //方法
        public void f1(){
            class inner{//局部内部类
                public void f2(){
                    System.out.println(name);
                }
            }
            //在f1()方法中(作用域)创建对象再访问
            inner inner = new inner();
            inner.f2();
        }
    }

    小结:

    (1)局部内部类定义在方法中/代码块

    (2)作用域在方法体或者代码块中

    (3)局部内部类本质仍然是一个类

    6> 外部其他类不能访问局部内部类,因为局部内部类地位是一个局部变量

    7> 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

    public class InnerClass {
        public static void main(String[] args) {
            Outer outer1 = new Outer();
            outer1.f1();
            System.out.println(outer1);
        }
    }
    class Outer{//外部类
        //属性
        private String name = "King";
        //方法
        public void f1(){
            class inner{
                String name = "Jerry";
                public void f2(){
                    System.out.println(name);
                    //Outer.this本质就是一个外部类的对象;机那个对象调用了f1(),Outer.this就是哪个对象
                    System.out.println(Outer.this.name);// outer1 = Outer.this
                }
            }
            inner inner = new inner();
            inner.f2();
        }
    }
    //打印结果:
    //Jerry
    //King
    //Outer@2133c8f8
    //Outer@2133c8f8

    3、匿名内部类

    匿名内部类本质是类,是一个内部类,该类没有名字,同时还是一个对象

    说明:匿名内部类是定义在外部类的局部位置,比如方法中、代码块中,并且没有类名

    1> 匿名内部类的基本语法

    new 类或接口 (参数列表) {
        //类体
    };

    2> 匿名内部类基本使用

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            Method method = new Method();
            method.m1();
        }
    }
    interface IA{
        void fly();
    }
    class Father{
        private String name;
        public Father(String name) {
            this.name = name;
            System.out.println("Father类的有参构造器被调用");
        }
        public void m1(){
            System.out.println("Father's name is " + name);
        }
        public void m2(){
            System.out.println("m2()");
        }
    }
    abstract class Son{
        public void m1(){
            System.out.println("m1方法");
        };
        public abstract void m2();
    }
    class Method{
        public void m1(){
            //基于接口的匿名内部类
            //ia1的编译类型=IA,运行类型为Mnthod$1(即外部类+$1)
            IA ia1 = new IA(){
                @Override
                public void fly() {
                    System.out.println("IA接口的fly方法");
                }
            };
            //底层原理为:
            //class innerClass$1 implements IA{
            //     @Override
            //     public void fly() {
            //     System.out.println("IA接口的fly方法");
            //     }
            // }
            //jdk底层创建匿名内部类Method$1,然后立即创建了Method$1实例,并且把实例地址返回给ia1
            //只不过此匿名内部类使用一次之后就没有了,但是ia1指向的对象还是在的
            ia1.fly();
            System.out.println("ia1的运行类型=" + ia1.getClass());
    
            //基于类的匿名内部类
            //father的编译类型Father、father的运行类型Method$2
            //("King")参数列表会传递给Father类的构造器
            Father father = new Father("King"){
                @Override
                public void m2() {
                    System.out.println("匿名内部类的m2()");
                }
            };
            System.out.println("father的运行类型=" + father.getClass());
            //m1、m2都可以调用
            father.m1();
            father.m2();
            //底层原理为:
            //class Method$2 extends Father{
            //    @Override
            //    public void m2() {
            //    System.out.println("匿名内部类的m2()");
            //    }
            //}
            //jdk底层创建匿名内部类Method$2同时继承Father类,然后立即创建了Method$2实例,并且把实例地址返回给father
            //只不过此匿名内部类使用一次之后就没有了,但是father指向的对象还是在的
            //如果Father类是一个抽象类,则匿名内部类的内部必须实现抽象类的方法
            //基于抽象类的匿名内部类
            Son son = new Son(){
                @Override
                public void m2() {
                    System.out.println("匿名内部类的m2");
                }
            };
            son.m1();
            son.m2();
        }
    }

    3> 因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,因此有两种可以调用匿名内部类方法的方式

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            Father father = new Father();
            father.f1();
        }
    }
    class Father{
        private String name = "King";
        public void f1(){
            //第一种调用方法
            AA a = new AA(){
                @Override
                public void m2() {
                    System.out.println("匿名内部类重写了m2");
                }
            };
            a.m1();
            a.m2();  //动态绑定机制====Father$1.m2();
            //第二种调用方法
            new AA(){
                @Override
                public void m2() {
                    System.out.println("匿名内部类重写了m2");
                }
            }.m2();  //可以直接调用,匿名内部类本身也是一个对象
        }
    }
    class AA{
        public void m1(){
            System.out.println("m1...");
        }
        public void m2(){
            System.out.println("m2...");
        }
    }

    4> 可以直接访问外部类的所有成员,包括私有的

    5> 不能添加访问修饰符,因为它的地位就是一个局部变量

    6> 作用域:仅仅在定义它的方法或代码块中

    7> 匿名内部类访问外部类成员访问方式为直接访问

    8> 外部其他类不能访问匿名内部类(因为匿名内部类地位是一个局部变量)

    9> 如果外部类和匿名内部类的成员重名时,匿名内部类访问默认遵循就近原则,如果想访问外部类的成员则可以使用(外部类名.this.成员)访问

    10> 匿名内部类最佳实践

    匿名内部类可以当作实参进行直接传递,简洁高效

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            //第一种
            Cellphone cellphone = new Cellphone();
            cellphone.alarmclock(new Bell(){
                @Override
                public void ring() {
                    System.out.println("懒猪起床了");
                }
            });
            cellphone.alarmclock(new Bell(){
                @Override
                public void ring() {
                    System.out.println("小伙伴上课了");
                }
            });
            //第二种
            new Cellphone(){
            }.alarmclock(new Bell(){
                @Override
                public void ring() {
                    System.out.println("懒猪起床了");
                }
            });
            new Cellphone(){
            }.alarmclock(new Bell(){
                @Override
                public void ring() {
                    System.out.println("小伙伴上课了");
                }
            });
    
        }
    }
    interface Bell{
        void ring();
    }
    class Cellphone{
        public void alarmclock(Bell bell){
            bell.ring();//动态绑定机制
        }
    }

    4、成员内部类

    成员内部类是定义在外部类的成员位置,并且没有static修饰

    1> 可以直接访问外部类的所有成员,包括私有的

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            new AA().f1();
        }
    }
    class AA{
        private int age = 10;
        public void m1(){
            System.out.println("m1...");
        }
        class BB{  //成员内部类
            public void m1(){
                System.out.println(age);
            }
        }
        public void f1(){
            new BB().m1();
        }
    }

    2> 可以添加任意访问修饰符(public、protected、默认、 private),因为它的地位就是一个类的成员

    3> 作用域和外部类的其他成员一样,为整个类体

    4> 成员内部类访问外部类成员的访问方式:直接访问

    5> 外部类访问成员内部类的访问方式:创建对象,再访问

    6> 外部其他类访问成员内部类的方式

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            AA aa = new AA();
            //第一种方式:将成员内部类当作AA类的成员
            AA.BB bb1 = aa.new BB();
            bb1.m1();  //输出10
            //第二种方式:定义一个方法,返回一个BB类对象
            AA.BB bb2 = aa.getBB();
            bb2.m1();  //输出10
            //第三种方式:new一个AA类,再在此基础上newBB类
            AA.BB bb3 = new AA().new BB();
            bb3.m1();  //输出10
        }
    }
    class AA{
        private int age = 10;
        public void m1(){
            System.out.println("m1...");
        }
        class BB{  //成员内部类
            public void m1(){
                System.out.println(age);
            }
        }
        public BB getBB(){  //定义一个方法,返回一个BB类对象
            return new BB();
        }
    }

    7> 如果外部类和内部类的成员重名时,内部类访问默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

    5、静态内部类

    静态内部类是定义在外部类的成员位置,并且有static修饰

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            new AA().m2();
        }
    }
    class AA{
        private int age = 10;
        private static int height = 175;
        public void m1(){
            System.out.println("m1...");
        }
        static class BB{  //静态内部类
            public void m1(){
                new AA().m1();
                System.out.println(height);
            }
        }
        public void m2(){
            new BB().m1();
        }
    }

    1> 可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员

    2> 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员

    3> 作用域与其他的成员一样,为整个类体

    4> 静态内部类访问外部类的方式:直接访问所有静态成员

    5> 外部类访问静态内部类的访问方式:创建对象,再访问

    6> 外部其他类访问静态内部类的方式

    package com.study;
    public class innerClass {
        public static void main(String[] args) {
            //第一种方式:静态内部类可以通过类名直接访问,前提满足访问权限
            AA.BB bb1 = new AA.BB();
            bb1.m1();
            //第二种方式:定义一个普通方法,返回一个静态内部类的实例
            AA aa = new AA();
            AA.BB bb2 = aa.getBB();
            bb2.m1();
            //第三种方式:定义一个static方法,返回一个静态内部类的实例
            AA.BB bb3 = AA.getBB_();
            bb3.m1();
        }
    }
    class AA{
        private int age = 10;
        private static int height = 175;
        public void m1(){
            System.out.println("m1...");
        }
        static class BB{  //静态内部类
            public void m1(){
                new AA().m1();
                System.out.println(height);
            }
        }
        public BB getBB(){ //定义一个普通方法,返回一个静态内部类的实例
            return new BB();
        }
        public static BB getBB_(){//定义一个static方法,返回一个静态内部类的实例
            return new BB();
        }
    }

    7> 如果外部类和静态内部类的成员重名时,静态内部类访问默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问

  • 相关阅读:
    OpenFlow Switch学习笔记(一)——基础概念
    Open vSwitch 给虚拟机网卡限流(QoS)
    MySQL字符集或字符序
    timestamp和datetime
    MySQL Audit日志审计
    sysbench0.4.12测试query_cache_size和query_cache_type
    MySQL 异地 双机房同步之otter
    keep running
    Linux Bonding
    自动化测试-2.seleniumIDE
  • 原文地址:https://www.cnblogs.com/zrh918/p/15748099.html
Copyright © 2011-2022 走看看