时尚、时尚、最时尚,爱不释手是JAVA

java是一门很好的语言,也是一门庞杂的系统,学习过程中要善于记录和积累。

一、java基础

1、重要概念

javaSE(标准版) javaEE(企业版) javaME(微型版)
JDK(java开发工具包)JRE(java运行环境)IDE(集成开发环境)

2、Java规范

(1)代码注释

概念: 可以对代码进行描述,说明某一段代码实现了什么功能,用了什么算法,执行的流程;
单行注释: //
多行注释: /* */
JavaDoc注释: /* /
JavaDoc注释用来生成java的API(Application Programming Interface,应用程序编程接口)文档,方便其他程序员利用自己的代码。在每个程序的最开始部分,一般都用Javadoc注释进行程序的总体描述以及版权信息。在主程序中可以为每个类、接口、方法、变量添加Javadoc注释,每个注释的开头部分先用一句话概括该类、接口、方法、变量所完成的功能,这句话应单独占据一行以突出其概括作用,在这句话后面可以跟随更加详细的描述段落。

/**
*This is an example of
* Javadoc
*
*@author HaiNing
*@version 2.0, 9/15/2016
*/
//单行注释
块注释
/*注释快*/

代码注释作用:虽然添加注释不会使一个设计低劣的程序变成好的程序,但是如果按照编程规范编写程序,并且为程序添加良好的注释,却可以帮助编写出设计优美、运行高效且易于理解的程序,尤其在多人合作完成同一项目时,编程规范非常重要。俗话说”磨刀不误砍柴工”,花费一点时间去适应一下Java编程规范是有好处的。

(2)标识符命名规范

标识符命名规则: ①由字母、下划线、$符、数字组成,数字不能打头、不能是Java关键字;
Java关键字
PS:Java标识符可以使用中文(unicode编码),但是编写的代码不具有通用性,外国人看不懂,且大 多数第三方类库都是用英文写的。
②Java标识符区分大小写;
使用范围: 包名、类名、方法名、变量名、常量名
① 包的命名
Java包的名字都是由小写单词组成。每一名Java程序员都可以编写属于自己的Java包,为了保障每个Java包命名的惟一性,最新的Java编程规范要求程序员在自己定义的包的名称之前加上惟一的前缀。由于互联网上的域名是不会重复的,所以程序员一般采用自己在互联网上的域名作为自己程序包的惟一前缀。
例如:net.frontfree.javagroup。
② 类的命名
类的名字必须由大写字母开头,一个单词中的其他字母均为小写。如果类名称由多个单词组成,则建议将每个单词的首字母均用大写,例如TestPage。如果类名称中包含单词缩写,则建议将这个词的每个字母均用大写,如:XMLExample。由于类是设计用来代表对象的,所以建议在命名类时应尽量选择名词。
③方法的命名(驼峰原则)
方法的名字的第1个单词应以小写字母开头,后面的单词则建议用大写字母开头(驼峰原则)。
例如:sendMessge()。
④ 变量的命名(驼峰原则)
变量的命名规范和方法的命名规范相同,而且为了避免阅读程序时造成迷惑,请在尽量保证在变量名称为一个单词的情况下,变量的命名尽可能明确。**注意:**1、类变量允许不进行初始化;2、方法内部的变量要进行初始化
⑤常量的命名
常量的名字应该都使用大写字母,并且指出该常量完整含义。如果一个常量名称由多个单词组成,则建议用下划线来分割这些单词。
例如:MAX_VALUE。

3、数据类型

(1)变量的含义

字面值:某个类型的合法取值,int i = 1;a是变量,5就是字面值;
小数默认的数据类型是double,float数据类型有一个后缀“f”或“F”;
long类型有一个后缀,为” l ” 或者” L “;
字符类型(char)要用单引号括起来。

①局部变量

概念: 在方法内部定义的变量
局部变量规则:
1、先赋值,后使用
局部变量没有默认初始值,需要手动赋值后才能使用。

public static void main(String[] args) {
    int a;
    System.out.println(a);//编译出错,因为局部变量a没有被初始化
}

2、局部变量作用域
局部变量的作用域是从定义它的位置开始到包含它的代码块结束。
同一作用域内不能有重名的局部变量。

public static void main(String[] args) {
    System.out.println(a);//编译出错,因为a变量还没有被定义
    int a =1;
}
public static void main(String[] args) {
    int a =1;
}
System.out.println(a);//超出a变量的作用域
public static void main(String[] args) {
    int a = 2;//作用域是整个main方法
    {
        //编译出错:Duplicate local variable a
        int a = 1;//作用域是整个代码块内部
        System.out.println(a);
    }
}
Q&A

辨析成员变量、局部变量和全局变量
局部变量:是指在程序中,定义在特定的方法或语句块的变量,是相对与全局变量而言的。
成员变量:在类体的变量部分中定义的变量,也称为属性,又分为实例变量和类变量(静态变量)。
成员遍历和局部变量的区别

public class Test{
  int a=0; //成员变量(全局变量)
  public static void main(String[] args){
  int b =0//局部变量
    }
}

1.定义的位置不同:成员变量直接定义在类内部,局部变量是定义某个方法体内部;
2.作用域不同:成员变量适用于整个类和与该类相关的类,局部变量只适用于该方法内部;
3初始值不同:成员变量可以不显式初始化它们可以由系统设定默认值,局部变量没有默认值,所以必须设定初始赋值;
4.不同方法可以有重名的局部变量;
5.如果类变量和局部变量重名,局部变量更有优先级;
成员变量又可以细分为:实例变量、类变量(静态变量)

public class Clothes 
{ 
    String id; //实例变量 
    private String colorType; //实例变量 
    private int size; //实例变量 
    private static String depart; //类变量(静态变量) 
    final String design="yangzi"; //常量 
}

实例变量和类变量的区别
1.实例变量:不用static修饰,它只能通过new对象调用而且所有对象的同一个实例变量是共享不同的内存空间的;
2.类变量:又叫静态变量,用static修饰,它可以直接用类名调用也可以用对象调用,而且所有对象的同一个类变量都是共享同一块内存空间的;

(2)基本类型

字符型:char;整型:byte、 short、 int、 long;浮点型: float、 double;布尔型:boolean

基本数据类型分类大小取值范围默认初始值
byte整型1字节 8bit【-128 127】0
int整型4字节 32bit【-2*10^31 2*10^31-1 】0
short整型2字节 16bit【-2*10^15 2*10^15-1 】0
long整型8字节 64bit【-2*10^63 2*10^63-1 】
char字符型2字节 16bit【-2*10^31 2*10^31-1 】
float浮点型4字节 32bit【3.402823e+38 ~ 1.401298e-45】
double浮点型8字节 64bit【1.797693e+308~ 4.9000000e-324】

简单说明:byte 8个bit 因为是有符号的所以取值范围(-128~127) ;
int和float 都是32个bit ;long和double都是64个bit;表示范围大的类型不能赋值给表示范围小的类型,例如:int a = 2; byte b; b = a;//会编译出错,int类型不能直接给byte类型赋值;

(3)类型转换

①强制类型转换
 int a = 2;  
 byte b; 
 b = (byte)a;
最后一句的执行过程:首先开辟byte类型的临时空间,然后将a的值转换成byte类型,放到刚开辟的临时空间,最后把临时空间的值赋值给b。PS:在转换过程中,并没有改变a变量的类型和值。
如果a的值超出了byte类型的取值范围(假如a变量的大小有4个字节,怎么转换成1个字节的byte类型),使用强制类型转换也能正常编译,java会把a变量4个字节中最后一个字节的值取出来放到临时空间,其他3个字节就在强制类型转换的过程中舍弃了。
int a = 150;  
byte b; 
b = (byte)a;
System.out.println(b);//其他3个字节就在强制类型转换的过程中舍弃了
System.out.println(a);//在转换过程中,并没有改变a变量的类型和值
②自动类型提升
//这种情况会报错
byte b1 = 1,b2 = 2;
a += b;//编译通过,自加没有自动类型提升问题
byte b3 = b1+b2;//编译出错,不能将int类型字面值赋给byte类型

以上代码的执行顺序:计算b1+b2的值–>保存到临时空间–>将临时空间的值赋给b3
问题出在:第二步“将b1+b2的值保存到临时空间”,Java在为临时空间选择数据类型时会自动提升为int类型;
修改:byte b3 =(byte) b1+b2;//强制类型转换
数据类型自动提升规律:
a和b做运算:如果a、b中有double,运算结果就是double类型
如果a、b中有float,运算结果就是float类型
如果a、b中有long,运算结果就是long类型
除了以上情况外,结果都是int(byte+byte,byte+short,char+byte);

Q&A

1、给char类型赋值数字
Java中对char型数据,在内存中存的就是整数,char在java中是unicode编码 ,字符 ‘A’ 对应的十进制数字是65,字符 ‘a’ 对应的十进制数字是97 ,所以char c1 = ‘A’ ; 等效于 char c1 = 65 ;
所以,char型数据可以和int型数据进行运算!比如 ‘a’ + 1 的结果如果用char型变量接收就是 ‘b’ , 用int型变量接收就是98 。虽然char型是以整数存储,但是和int型还是有一定区别的,表现在取值范围上,
char型 占2个字节 无符号 范围是0~65535 。所以char c = 65536 就会报错了,因为越界了。
2、byte和char的区别:
①.Char是无符号型的,可以表示一个整数,不能表示负数;而byte是有符号型的,可以表示-128到127 的数;
②.char可以表中文字符,byte不可以;
③.char、byte、int对于英文字符,可以相互转化

public class DataTypeTest {

    public static void main (String[] args){
        DataTypeTest dtt = new DataTypeTest();
        dtt.testByte();
        dtt.testChar();
        dtt.testChange();

    }
    /**实验byte
     * Char是无符号型的,可以表示一个整数,不能表示负数;
     * 而byte是有符号型的,可以表示-128—127 的数
     */
    public void testByte(){
        byte b1 = 1;
        byte b2 = -1;
        byte b3 = -128;
        byte b4 = 127;
        char c = (char) -3;//char不能识别负数,强转一下编译器不会报错,但是仍无法识别
        System.out.println(b1);//1
        System.out.println(b2);//-1
        System.out.println(b3);//-128
        System.out.println(b4);//127
        System.out.println(c);//?
        System.out.println("------------------");
    }
    /**实验char
     * char可以表示中文字符,byte不可以
     * char类型在存储的时候就是整数,对应一张Unicode编码表
     * 'A'对应65,而'a'对应97,所以char类型可以和int类型进行运算
     */
    public void testChar(){
        char c1 = '中', c2 = '国' , c3 = 1;
        byte b1 = (byte)c1;//45
        int i = '中'+1;//20014
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(b1);//问题:输出的数字的含义
        System.out.println("i="+i);
        System.out.println("------------------");
    }

    /**
     * char、byte、int对于英文字符,可以相互转化
     */
    public void testChange(){
        byte b = 'b';//98
        char c1 = (char)b;//b
        char c2 = 'c';//c
        int i = c2;//99
        System.out.println(b);
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(i);
        System.out.println("------------------");
    }
}    

(4)引用数据类型

组成: String类型、自定义类、接口、抽象类

①String类型

**概述:**String类型是一种对象类型,不属于八种基本数据类型;
特征: 1、String类型的字面值放在双引号里;String str = “abc”;
2、String类型可以做加法运算(字符串的拼接);String str1 =str +”efg”;
3、任何类型与String类型相加,结果都为String类型;

String str = "abc";
int i = 5;
String str1 = str+ "efg";//字符串的拼接
String str2 = str + i;//String类型与基本类型相加
System.out.println(str1);//abcefg
System.out.println(str2);//abc5
②、数组

概念:数组是用来管理多个同类型的变量的。一个数组,是一段连续的内存空间,这段内存
空间中,能够保存多个同类型的值。
数组的定义:

/**
* 声明数组
* 定义了一个数组变量a,此时没有分配连续的内存空间
*/
int[] i;
float f [];
/*
*数组在内存中的表示
*1、new就是在内存中开辟了一段连续的内存空间
/2、10个int类型的值 ,因此是40个字节
*这40个字节每个字节都有自己的一个内存地址,其中40个字节中的第一个字节的内存地址,被称之为“首地址”
/3、将首地址赋值给i,换句话说:数组变量保存的是数组中的首地址
*/
/*i = new int[10]; 
f = new float[10]; 
//在声明的同时申请内存空间
char[] c = new char[10];*/

/**
* 赋值
*/
/*i[0] = 0;
i[1] = 1;
//循环赋值、取值
for(int j=0;j<i.length;j++){
    i[j] = j;
}
for(int t=0;t<i.length;t++){
    System.out.println(i[t]);
}
//在声明数组的同时申请空间并赋值(数组的显式初始化)
//数组的显式初始化不定义数组的长度
int[] i1 = {};
int[] i2 = new int[]{};
String[] s ={"我","是","好","人"};
float[] f1 = new float[]{2.1f,2.2f};
System.out.println(f1[0]);*/

遍历数组:
数组元素和局部变量不同,数组元素可以在没有赋初始值的情况下就使用。此时,这些数组元素也有特定
的值,这就是元素的“默认值”。在为数组分配空间的时候,数组的元素会被JVM 赋予默认值。
对于基本类型来说,byte、short、int、long 这四种整数类型,默认值为0;float 和double 这两种小数类型,默认值为0.0,boolean 默认值为false,char 默认值也为0。注意,char 类型的0不是指的字符‘0’,而是指的编码为0。
对于对象类型的数组来说,默认值为null。

/**
* 强制for循环(foreach)
* for(变量类型 变量名:被遍历 ){}
*/
int[] nums = new int[5];
int sum = 0;
int max = nums[0];
Scanner sc = new Scanner(System.in);
for(int i=0;i<nums.length;i++){
    System.out.println("请输入一个整数....");
    nums[i] = sc.nextInt();
}
for(int i:nums){
    System.out.println(i);
}

二维数组: 数组的数组
定义:int[][] a 或者 int a [][]

int[][] a
//定义了一个两行三列的数组
a = new int[2][3];
/*
*二维数组的初始化和遍历
*/
int[][] nums = {{1,2,3},{4,5,6},{7,8,9}};
for(int i=0;i<nums.length;i++){//行
    for(int j=0;j<nums.length;j++){//循环每行值
        System.out.print(nums[i][j]);
    }
    System.out.println();//换行
}

实例:

/**
* 简单排序
*/
/*int temp = 0;
int[] nums = new int[]{4,3,2,1,0};
for(int i=0;i<nums.length;i++){
    for(int j=i+1;j<nums.length;j++){
        if(nums[i]>nums[j]){
            //System.out.println(j+"  "+nums[j]);
            temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    System.out.println(nums[i]);
}*/
/**
* 冒泡排序
* 对数组相邻的元素两进行比较,如果左边值大于右则交换位置 
*/
int temp = 0;
int[] nums = new int[]{4,3,2,1,0};
for(int i = 0;i < nums.length-1;i++){
    for(int j = 0;j<nums.length-i;j++){
        if(nums[j] > nums[j+1]){
            temp = nums[j];
            nums[j] = nums[j+1];
            nums[j+1] = temp;
        }
    }
}
System.out.println(Arrays.toString(nums));
Q&A

String、StringBuffer和StringBuilder三者的区别:
1、String是常量,StringBuffer和StringBuilder是变量,修改时不生成新的对象,内存使用上占优势;
2、StringBuffer是线程安全的,StringBuilder非线程安全;
3、执行速度:(StringBuilder,StringBuffer)>String ;
综上所诉:1.当数据量不是很大的时候用String;2.单线程用StringBuilder;3.多线程用StringBuffer
三者区别详解

4、函数

(1)概念:函数(Function)也叫方法(Method),是定义在类中的具有特定功能的一段独立小程序;

(2)定义:

修饰符 返回值类型  函数名(参数列表) {
     //函数体
     return 返回值;
}

要点:1、函数定义在类里,其他函数的外边;
2、return语句出来可以返回值,还能控制流程的跳转;
3、如果一个函数不需要返回值,需要把返回值类型定义为void;

(3)行参和实参:

行参出现在函数的定义里,类似于局部变量,在整个函数体内部可以使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。实参和形参的功能是数据传递,在发生函数调用时,主调函数实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传输。
1、形参变量只有在被调用时才分配内存空间,调用结束就释放空间,所以形参只在函数内部有效,函数调用结束后返回主调函数则不能再使用形参变量;
2、实参可以是常量、变量、表达式、函数等,无论实参是何种类型的值,在进行函数调用时,它都必须有确定的值,以便把这些值传递给形参;
3、形参和实参的数量、类型、顺序应严格一致,否则会发生类型不匹配的错误。
4、函数调用过程中发生的数据传递是单向的,即只能实参的值传递给形参,而不能不形参的值反向穿递给实参。因此在函数调用过程中形参的值会发生改变,而实参中的值不会变化。
public class Test {

    public static void main(String[] args) {
        int intParam = 1;
        Integer objParam = 1;
        //实参传递到函数中
        addParam(intParam);
        addObjParam(objParam);
        //实参值没有改变
        System.out.println(intParam);
        System.out.println(objParam);
        //用函数的返回值重新给实参赋值
        intParam = addParam(intParam);
        objParam = addObjParam(objParam);
        System.out.println(intParam);
        System.out.println(objParam);


        ArrayList<String> list = new ArrayList<String>();
        list.add("1111");
        list.add("2222");
        list.add("3333");
        System.out.println("size1: "+list.size());
        delListSize(list);
        System.out.println("size3: "+list.size());


        ArrayList<String> list2 = new ArrayList<String>();
        list2.add("1111");
        list2.add("2222");
        list2.add("3333");
        System.out.println("sizeA: "+list2.size());
        addListSize(list2);
        System.out.println("sizeC: "+list2.size());
    }

    public static int addParam(int i) {
        i++;
        return i;
    }

    public static Integer addObjParam(Integer i) {
        i++;
        return i;
    }

    public static void delListSize(ArrayList<String> li){
        li.remove(0);
        System.out.println("size2: "+li.size());
    }

    public static void addListSize(ArrayList<String> li){
        //创建了一个新对象即是对新对象进行操作
        li = new ArrayList<String>();
        li.add("4444");
        System.out.println("sizeB: "+li.size());
    }

}

(4)按值传递和引用传递:

参数按值传递,传递的是值的拷贝,也就是说传递后就互不相关了,不会影响原值。
参数按引用传递,传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间),因此,外部对引用对象所做的改变会反映到所有的对象上。
按值传递和引用传递详解

(5)函数的作用:

1、减少代码的冗余;
2、提高代码的可维护性;
3、让程序更加灵活;
4、提高代码的可重用性;
题目:求阶乘和?

二、Java进阶

1、类和对象

(1)概念

对象:万物皆对象(一切客观存在的事物都能称之为对象)
类:“对象” 指的是客观存在事物,因此每一个对象在客观世界都能找到与之对应的客观事物,而“类”是对一类事物的概括和抽象,是对象创建的模板。

(2)Java的构造方法

1.程序运行的时候构造方法就被加载;
2.每个类都有构造方法,如果程序员给类提供构造方法,编译器会自动创建一个默认的构造方法;
3.构造方法重载类似于方法的重载,一个类可以有多个构造方法,但参数列表必不同。

(3)static(静态变量、静态方法和静态初始化块)

含义:“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
Java中static方法不能被覆盖, 因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。
static方法跟类的任何实例都不相关,所以概念上不适用。
静态变量和静态方法:
1.静态变量可以直接被类和对象调用;
2.静态方法中不能直接调用非静态变量和非静态方法,可以通过创建对象调用;
3.普通方法中可以 直接调用类中的静态和非静态变量;
实例:

public class HelloWorld {
    static String s = "你好,静态变量!";// 静态变量
    String s1 = "你好,我是非静态变量!";

    public static void say() {
        System.out.println("你好,我是静态方法,我被调用了");
        // System.out.println(s1); 这句编译器会报错,因为静态方法中不能调用非静态变量
        // 如果想在静态方法中调用非静态变量,可以创建类的对象,通过该对象调用非静态变量
        HelloWorld hw = new HelloWorld();
        System.out.println("我是通过在静态方法中实例化类,通过类调用静态变量:" + hw.s1);

        // 静态方法中不能直接调用非静态方法,需要创建对象来调用
        // shuo(); 编译器会报错
        hw.shuo();
    }

    public void shuo() {
        System.out.println("你好,我是普通方法,我被调用了");
        System.out.println(s);
        System.out.println(s1);
    }

    public static void main(String[] args) {
        // 类直接调用静态变量
        System.out.println(HelloWorld.s);
        // 对象调用静态变量
        HelloWorld hw = new HelloWorld();
        System.out.println(hw.s);

        // 类直接调用静态方法
        HelloWorld.say();
        // 对象调用静态方法
        hw.say();

        // shuo();静态方法中不能直接调用非静态方法

    }
}

静态初始化块:
静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量。
实例:

public class HelloWorld {
    static int num1;
    int num2;
    int num3;


    static {
        num1 = 1;
        System.out.println("通过静态初始化块赋值");
    }
    {
        num2 = 2;
        System.out.println("通过初始化块赋值");
    }
 public HelloWorld(){
     num3 = 3;
     System.out.println("通过构造方法赋值");
 }

    public static void main(String[] args) {
        HelloWorld hw = new HelloWorld();//实例化类
        System.out.println("num1:"+ num1);
        System.out.println("num2:"+ hw.num2);
        System.out.println("num3:"+ hw.num3);
        HelloWorld hw2 = new HelloWorld();//再次实例化
    }
}

实例总结:
1、执行顺序上,先执行静态代码块,在执行普通代码块,最后执行构造方法。
2、注意:静态代码块只在类加载时执行一次,所以在再次创建对象是静态初始化块并未再次执行。
3、静态初始化块只能给静态变量赋值,不能初始化普通的成员变量;

2、面向对象

(1)面向对象编程(OOP)的特点

面向对象的编程优点:代码开发模块化,易于维护和理解,代码复用性,增强代码的可维护性和灵活性,

①、封装

将对象封装成类,类隐藏内部的实现细节,统一提供属性和方法,便于代码的维护。
java中的访问修饰符:
1、private:只有本类内部可以访问,切不能被访问(继承);
2、默认(不加访问修饰符),只能在本类和同一包中使用
3、protected:能在本类,同一包(package)下,和该类的子类被访问(该子类可以不
4、public:公开的,都可以被访问(继承)

②、继承

继承是类与类之间的一种关系,子类可以使用父类的属性和方法(父类的属性和方法不能是privata),增加了代码的复 用性,也为多态的实现提供了前提。
注意:1、java中只能实现单继承;
2、super()代表在子类中调用父类的构造方法;
3、this.属性/this.方法,表示访问本类中的属性和方法,this()代表调用本类的构造方法;
4、 类与类之间的关系:继承关系;依赖关系(该类对象作为另 一个类中一个方法的参数) 实现关系(实现是类与接口之间最常见的关系)类与类之间的关系详解
5、final关键字:可以修饰类、方法、属性和变量,修饰类该类不能被继承,修饰方法该方法不能被覆盖,修饰属性该属性不能自动初始化,修饰变量该变量只能赋一次值
6、java中继承的执行顺序:父类属性初始化–>父类构造方法–>子类属性初始化–>子类构造方法

class A {
    static {
        System.out.println("父类静态初始化块赋值");
    }
    {
        System.out.println("父类初始化块赋值");
    }
 public A(){
     System.out.println("父类构造方法赋值");
 }
}

class B extends A{
    static {
        System.out.println("子类静态初始化块");
    }
    {
        System.out.println("子类初始化块");
    }
 public B(){
     System.out.println("子类构造方法");
 }
}
public class Test {
    public static void main(String[] args) {

        B b1 = new B();//实例化类
        System.out.println();
        B b2 = new B();//再次实例化
    }
}

执行结果:(即为顺序)
父类静态初始化块赋值
子类静态初始化块
父类初始化块赋值
父类构造方法赋值
子类初始化块
子类构造方法

父类初始化块赋值
父类构造方法赋值
子类初始化块
子类构造方法

③、多态

重用、重载、动态调用构成了多态。
体现:子类生产父类对象Animal A = new Cat();

class Father{
    public void fun1(){
        System.out.println("父类的fun1()");
    }
    public void fun2(){
        System.out.println("父类的fun2()");
    }

}
class Son extends Father{
        public void fun1(){
        System.out.println("子类的fun1()");
    }

    public void fun2(int i){
        System.out.println("子类的fun2(int i)");
    }
}
public class duotai {
    public static void main(String[] args) {

        Father f = new Father();
        f.fun1();
        f.fun2();
        System.out.println();

        Son s = new Son();
        s.fun1();
        s.fun2(1);
        s.fun2();
        System.out.println();

        Father f1 = new Son();
        f1.fun1();//父类的fun1()被重写/覆盖,所以调用的是子类的fun1()
        f1.fun2();//父类中没有定义fun2(int i)所以不能被调用
    }

}

以上实例说明:
1.子类生成父类对象,子类重写/覆盖(返回值类型、方法名和参数都相同但方法的内部实现不同)父类的方法,调用时直接调用子类的方法;
2.子类重载(返回值和方法名相同,参数不同)父类的方法,根据参数值调用子类方法;
3.子类生产父类对象,调用的方法父类中必须要有相应的实现,在子类中被重写。
关于多态的一个经典实例:

class A {
    public String show(D obj){
           return ("A and D");
    } 
    public String show(A obj){
           return ("A and A");
    } 
} 
class B extends A{
    public String show(B obj){
           return ("B and B");
    }
    public String show(A obj){
           return ("B and A");
    } 
}
class C extends B{} 
class D extends B{}
public class jingdianduotai {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        System.out.println(a1.show(d));//A and D
        System.out.println(a1.show(b));//A and A
        System.out.println(a1.show(c));//A and A
        System.out.println();
        System.out.println(a2.show(d));//A and D
        System.out.println(a2.show(a1));//B and A,子类重写了父类的方法
        System.out.println(a2.show(b));//B and A
        System.out.println(a2.show(c));//B and A
        System.out.println();
        System.out.println(b.show(b));//B and B  

        /*
         * 1.先在类B中招找方法show(c)
         * 2.没有
         * 3.再找b.show(super(c))即b.show(b)
         * 4.所以为B and B 
         */
        System.out.println(b.show(c));
        System.out.println(b.show(d));//A and D,子类继承了父类的方法
    }

}

引用类型转换:
1.向上类型转换(隐式/自动类型转换),小类型到大类型转换(无风险)Animal animal = new Dog();;
2.向下类型转换(强制类型转换),大类型到小 类型(存在方向)Animal animal = new Dog();Dog dog = (Dog)animal;与Dog dog = new (Dog)Animal();不同,后者不能进行类型转换。用 instanceof关键字验证(animal instanceof Cat)是否能进行类型装换。

class Dog extends Animal{
    public void eat(){
        System.out.println("狗吃骨头");
    }
    public Dog(){
        System.out.println("Dog执行了!!");
    } 
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public Cat(){
        System.out.println("Cat执行了!!");
    } 
}
public class TestInstanceOf {

    public static void main(String[] args) {
        Animal animal = new Dog();
        if(animal instanceof Dog){
            Dog dog = (Dog)animal;
        }else{
            System.out.println("Dog-->animal无法进行类型转换!");
        }

        if(animal instanceof Cat){
            Cat cat = (Cat)animal;
        }else{
            System.out.println("Cat-->animal无法进行类型转换!");
        }
    }

}

多态总结:
1、方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现;
2、如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写(Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading);
3、Overloaded的方法是可以改变返回值的类型。

3、抽象类和接口

①抽象类

概念:抽象类是约束子类必须拥有哪些方法,并不关注子类的实现
实例:

abstract class A{
    public abstract void doSomthing();//定义抽象方法修饰符必须是public(缺省值)或protected
    public  void fun(){//抽象类中的非抽象方法
        System.out.println("Im A.run(),Helllo");
    }
}
class B extends A{
    public void doSomthing() {//子类必须实现父类的抽象方法
        System.out.println("Im B,doSomthing!");
    }
    public  void fun(){
        System.out.println("Im B.run(),Hello!");
    }
}

class C extends A{
    public void doSomthing() {
        System.out.println("Im C,doSomthing!");
    }
}

public class Test{
    public static void main(String[] args) {
        A a1 = new B();
        A a2 = new C();
        B b = new B();
        C c = new C();
        System.out.println(a1);
        System.out.println(a2);
        System.out.println(b);
        System.out.println(c);
        a1.fun();
    }
}

抽象类总结:1.包含抽象方法的类一定抽象类,定义了抽象类(abstract class A)不一定有抽象方法,抽象类中也可以定义普通方法;2.抽象类必须用public或protected修饰;3.抽象类不能用来创建对象;4.如果一个类继承了抽象类则子类必须实现父类的所有抽象方法方法。

②接口

概念:接口泛指供别人调用的方法或者函数
实例

interface A{
    String s="sd";//接口中变量被隐式的指定为 publin static final
    public void doSomething();//接口中所有方法被隐式的指定为public abstract(抽象方法),所以说接口中只有抽象方法
}
interface B{
    public void playSomething();
}
class C implements A,B{
    public void doSomething(){//实现类必须实现接口所有的方法

    }
    public void playSomething() {

    }
}

**接口总结:**1、接口中的方法都是抽象方法;
2、一个类可以实现多个接口,一个非抽象的类实现了某个接口就必须实现接口中的所有方法(换句话说:对于遵循某 个接口的抽象类,可以不实现该接口中的抽象方法);
抽象类和接口的区别:
以下实例实现了一个会报警门,门的开关是门这一类事物特有的属性,但报警并不是(所有的门并不一定都是会报警的门),报警仅仅可以说是一种行为,一种门可能会有也可能不会有的行为,所以用接口去定义这种行为,你的门有报警功能就去实现(implements)这个接口呗,反之这不去实现。
实例

abstract class Door {
    abstract void open();

    abstract void close();
}

interface Alarm {
    void Alarm();
}

class AlarmDoor extends Door implements Alarm {
    public void open() {
        System.out.println("来门!");
    }

    public void close() {

        System.out.println("关门!");
    }

    public void Alarm() {
        System.out.println("报警!");
    }
}

区别总结:1.抽象类里可以有非抽象方法,而接口内的方法都是抽象的(public abstract);
2.抽象类多用来对一类事物共有属性的抽象,接口只是提供对扩展行为的;
3.接口里定义的属性都是final的,接口定义的方法只能是public ;
4,一个类可以实现多个接口但只能继承一个抽象类;
抽象类和接口详解

4、常用类

(1)Object类

Object()
默认构造方法
clone()
创建并返回此对象的一个副本。
equals(Object obj)
指示某个其他对象是否与此对象“相等”。
finalize()
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
getClass()
返回一个对象的运行时类。
hashCode()
返回该对象的哈希码值。
notify()
唤醒在此对象监视器上等待的单个线程。
notifyAll()
唤醒在此对象监视器上等待的所有线程。
toString()
返回该对象的字符串表示。
wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
wait(long timeout)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

(2)自动装箱与拆箱

①概念:

基本数据类型和包装类之间的自动转化,java是面向对象的语言,对象类型可以承载更多的信息和操作,另外包装类都实现类Compareable接口可以实现对象之间的比较。

②包装类:

byte char boolean short int long float double
Byte Character Boolean Short Integer Long Float Double
举例:

Integer i = 100;//自动装箱
int t = i;//拆箱,实际进行的操作是 t=i.intValue();

//在运算的时候,也可以进行自动拆箱
Integer i = 100;
System.out.println(i++);

equals() 比较的是两个对象的值(内容)是否相同。
“==” 比较的是两个对象的引用(内存地址)是否相同,也用来比较两个基本数据类型的变量值是否相等。
自动拆装箱详解

5、集合框架

java集合类中基本的接口:
Collection:是集合类的上级接口,继承与他的接口主要有Set 和List.
List:以特定次序来持有元素,可有重复元素;
Set:无法拥有重复元素,内部排序(无序);
Map:键值对key–value,value可多值
集合类特性(几个常用类的区别)
java集合类树状图
集合类特性(几个常用类的区别):
集合框架个实现类的区别
ArrayList: 元素单个,效率高,多用于查询 Vector: 元素单个,线程安全,多用于查询 LinkedList: 元素单个,多用于插入和删除
HashMap: 元素成对,元素可为空 HashTable: 元素成对,线程安全,元素不可为空
WeakHashMap: 是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收
Iterator与ListIterator有什么区别:
1. Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。 
2. Iterator只能正向遍历集合,适用于获取移除元素。ListIerator继承Iterator,可以双向列表的遍历,同样支持元素的修改。比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
Collection 和 Collections的区别:
Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作
ArrayList和Vector的区别:
ArrayList与Vector主要从二方面来说.
一.同步性:
Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的。
二.操作:
由于Vector支持多线程操作,所以在性能上就比不上ArrayList了。
三.数据增长:
ArrayList和Vector都有一个初始的容量大小,当存储进去它们里面的元素个数超出容量的时候,就需要增加ArrayList和Vector的存储空间,每次增加存储空间的时候不是只增加一个存储单元,是增加多个存储单元。
Vector默认增加原来的一倍,ArrayList默认增加原来的0.5倍。

6、异常处理

异常处理的知识架构:

这里写图片描述

三、java高级

1、IO流

这里写图片描述
IO框架知识架构
IO流And冒泡排序

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class IODemo {
    /**
     * @param 把数组写入到文件中
     * @throws IOException 
     */
    public void writerFile() throws IOException{
        String str = "";
        FileOutputStream fos = new FileOutputStream("result.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        BufferedWriter bw = new BufferedWriter(osw);
        int [] myIntArr  = new int[]{10,-2,5,44,6,-10}; 
        for(int i=0;i<myIntArr.length-1;i++){
            str+=myIntArr[i]+",";
        }
        str = str.substring(0,str.length()-1);
        bw.write(str);
        bw.close();
    }

    public void readFile() throws IOException{
         String str1 = "";
         String str = "";
        FileInputStream fis = new FileInputStream("result.txt");
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);
        //从文件中读出字符串
        str = br.readLine();
        //将字符串转换成数组
        String[] myStrArr = str.split(",");
        //把String数组转换成int数组
        int[] myIntArr = new int[myStrArr.length];
        for(int i=0;i<myStrArr.length;i++){
            myIntArr[i] = Integer.parseInt(myStrArr[i]);
        }
        sort(myIntArr);
         for (int i = 0; i < myIntArr.length; i++) {  
                 str1 += myIntArr[i]+",";  
            }  
            str1 = str1.substring(0, str1.length()-1);  
            System.out.print(str1);  
            br.close();  
    }

    public static void sort(int[] data){//从小到大(冒泡排序)
        int temp =0;
        for(int i=0;i<data.length;i++){
            for(int j=i+1;j<data.length;j++){
                if(data[i]>data[j]){
                    temp=data[j];
                    data[j]=data[i];
                    data[i]=temp;
                }
            }
        }

    }
    public static void main(String[] args) throws Exception {
        new IODemo().writerFile();
        new IODemo().readFile();
    }

}

2、多线程

基本概念:

进程是程序(任务)的执行过程,说明:1、进程是动态的;2、它持有资源(内存。。。)和线程。
线程是进程的一个执行序列,是系统中最小的执行单元,一个进程可以有多个线程,线程共享进程的资源。

创建线程:

①继承Thread类
class MyThread extends Thread {
    private int ticketsNum = 5;
    private String name;
    public MyThread(String name){
        this.name = name;
    }
    public void run() {
        while(ticketsNum>0){
            ticketsNum--;
            System.out.println(name+"卖了1张票,剩余票数为:"+ticketsNum);
        }
    }
}
public class TicketsThread {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("窗口1");
        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");

        //启动线程
        mt1.start();
        mt2.start();
        mt3.start();

    }
}
②实现Runnable接口
class MyThread implements Runnable {
    private int ticketsNum = 5;
    public void run() {
        while (ticketsNum > 0) {
            ticketsNum--;
            System.out.println(Thread.currentThread().getName() + "卖了1张票,剩余票数为:" + ticketsNum);
        }
    }
}

public class TicktesRunnable {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //创建三个线程
        Thread th1 = new Thread(mt,"窗口1");
        Thread th2 = new Thread(mt,"窗口2");
        Thread th3 = new Thread(mt,"窗口3");

        //启动三个线程
        th1.start();
        th2.start();
        th3.start();

    }

}
③应用程序可以使用Executor框架来创建线程池

**比较:**Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷;Runnable的代码可以被多个进程(Thread实例)共享,适合于对个线程处理同一资源的情况

线程在执行过程中,可以处于下面几种状态:

就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取锁。
死亡(Dead):线程完成了执行。

线程的生命周期:

创建状态:当New出一个Thread后就进入了创建状态
就绪状态:创建了线程后,调用了线程的start()方法(注意:此时线程只是进入了线程队列,等待获取CPU服务,具备了运行条件但不一定真正运行了);
运行状态:一旦线程获得了CPU资源,便进入运行状态,就开始执行run()方法里面的逻辑;
阻塞状态:一个正在执行的线程由于某些原因暂时让出了CPU资源,暂停了执行便进入了阻塞状态,如调用了sleep()方法;
终止状态:线程的run()方法执行完毕,或者线程调用了stop()方法(该方法被淘汰),线程便进入终止状态;

互斥与同步

线程间协作:wait()、notify()、 notifyAll()详解

public class Test {
    public static Object lock = new Object();
    static class Thread1 implements Runnable{
        public void run() {
            synchronized (lock) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程:"+Thread.currentThread().getName()+"获取了锁");
            }
        }

    }
    static class Thread2 implements Runnable{
        public void run() {
            synchronized (lock) {
                lock.notify();
                System.out.println("线程:"+Thread.currentThread().getName()+"调用了lock.notifyAll()");

            }
            System.out.println("线程:"+Thread.currentThread().getName()+"释放了锁");
        }

    }
    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        new Thread(t1,"线程1").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Thread2 t2 = new Thread2();
        new Thread(t2,"线程2").start();
    }
}

同步与互斥的实现: synchronized(intrinsic lock),synchronized也可以修饰方法,给代码加上锁,禁止其他访问保证只有一条能访问

class MyThread implements Runnable {
    private int ticketsNum = 100;
    public synchronized void run() {
        while (ticketsNum > 0) {
            ticketsNum--;
            System.out.println(Thread.currentThread().getName() + "卖了1张票,剩余票数为:" + ticketsNum);
        }if(ticketsNum <= 0){
            System.out.println("票已售完!!");
        }
    }
}
public class TicktesRunnable {

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //创建N个线程
        /*for(int i=0;i<10;i++){
            new Thread(mt,"窗口"+i).start();
        }*/
        new Thread(mt,"窗口1").start();
        new Thread(mt,"窗口2").start();
        new Thread(mt,"窗口3").start();
    }
}

守护线程

(运行在后台,为其他前台线程服务)
特点:一旦所有的用户线程都结束运行,守护线程会随JVM一起结束工作
应用:数据库连接池中的监测线程;JVM虚拟机启动后的监测线程
最常见的守护线程:垃圾回收线程
怎么设置守护线程:调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程(注意事项:setDaemon(true)必须在start()方法之前调用;守护线程中产生的新线程也是守护线程;并不是所有的任务都可以分配给守护线程来执行,比如读写操作和计算逻辑)

class DaemonThread implements Runnable{
    public void run() {//线程运行的方法
        System.out.println("进入守护线程"+Thread.currentThread().getName());
        try {
            writeToFile();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("退出守护线程"+Thread.currentThread().getName());
    }
    private void writeToFile() throws Exception {//向文件中写数据的方法
        File fileName = new File("c:"+File.separator+"daemo.txt");
        OutputStream os = new FileOutputStream(fileName,true);
        int count = 0;
        while(count<999){
            os.write(("\r\nword"+count).getBytes());
            System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入了word"+count++);
            Thread.sleep(1000);
        }
    }
}
public class ShouHuThread {//测试类
    public static void main(String[] args) {
        System.out.println("进入主线程"+Thread.currentThread().getName());
        DaemonThread daemonThread = new DaemonThread();
        Thread thread = new Thread(daemonThread);
        thread.setDaemon(true);
        thread.start();
        Scanner sc = new Scanner(System.in);
        sc.next();//没有键盘操作是主线程就会被阻塞,当键盘有输入时主线程就会解除阻塞继续执行直到结束,主线程(唯一的用户线程)结束后,守护线程也会退出运行,所以读写操作就不完整了
        System.out.println("退出主线程");
    }
}

死锁(deadlock)

两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中。
如何确保N个线程可以访问N个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

3、网络编程

4、反射

5、Java设计模式

(1)单例模式

目的:保证整个应用程序中某个类只有一个实例
使用场景:有些对象只需要一个就够了:配置文件,工具类,日志对象,线程池,缓存等。
实现思路:定义一个类,它的构造方法是私有的,有一个私有的静态的该类的变量在初始化的时候就实例化,通过一个公有的静态的方法获取该对象。
两种实现方式:

  //单例模式的第一种形式---饿汉模式    
 public class Singleton01 {    

     // 1.私有的构造方法,目的:不允许外部直接创建对象Singleton01 s01 =new Singleton01();    
     private Singleton01() {    
     }    

     //2.创建类的唯一实例,使用private static修饰    
     private static Singleton01 instance = new Singleton01(); //当类加载的时候,这个实例就被创建了   

     // 3.提供一个公有的获取实例的方法,使用public static(加上static让这个方法变成类方法)     
     public static Singleton01 getInstance() {    
         return instance;    
     }    
 }  

public class Test(){
     public static void main(String[] args){
       //Singleton01 s01 = Singleton01.instance;//通过 类名.成员名的方式创建实例(当创建的唯一实例被private修饰后这种方法就不能创建实例了)
       //Singleton01 s02 = Singleton01.instance; 
       Singleton01 s03 = Singleton01.getInstance(); 
       Singleton01 s04 = Singleton01.getInstance();
       if(s01==s01){
           System.out.println("是同一个实例");
          }else{
            System.out.println("不是同一个实例");
                   }        
 }
}

改进后的单例模式:

//单例模式的第二种形式---懒汉模式 
public class Singleton02 {    

     // 私有的静态的类变量    
     private static Singleton02 instance = null;    

     // 私有的构造方法    
     private Singleton02() {    
     }    

     // 静态的公有的方法    
     public static Singleton02 getInstance() {    
         if (instance == null) {    
             instance = new Singleton02();    
         }    
         return instance;    
     }    
 }  

饿汉模式在类被加载的时候就将自己实例化了,从资源利用率的角度来说,懒汉单例比饿汉单例的效率更高。

(2)工厂模式

概念:用工厂方法代替new,实例化对象;工厂模式包括工厂方法模式和抽象工厂模式,抽象工厂模式是工厂方法模式的扩展
实现思路:定义一个接口来创建对象,但是让子类来决定哪些类需要被实例化,工厂方法把实例化的工作推迟到子类中去实现;
使用场景:有一组类似对象需要创建;在编码是不能预见需要创建那种类的实例,系统需要考虑扩展性,不应依赖于产品类实例如何被创建、组合和表达的细节。
实例:

/*
 * 接口类
 */
    interface HairInterface {
        public void draw();

    }

    /*
     * 实体类1
     */
    class LeftHair implements HairInterface {

        public void draw(){
            System.out.println("左偏分");
        }
    }

    /*
     * 实体类2
     */
    class RightHair implements HairInterface {


        public void draw(){
            System.out.println("右偏分");
        }
    }

    /*
     * 工厂类提供三种工厂方法
     */
    class HairFactory {
        //第一种:机械重复的对比之后直接new出对应的类(如果新添加一个类还要重写该方法)
        public HairInterface getHair(String key){
            if("left".equals(key)){
                return new LeftHair();
            }else if("right".equals(key)){
                return new RightHair();
            }
            return null;
        }
        //第二种:通过java的反射找到相应的类(这里需要输入类的完整路径名:com.sunny.project.rightHair诸如此类。。)
        public HairInterface getHairByClass(String className){//根据类名来生成对象(java的反射)
            HairInterface hair = null;
            try {
                hair = (HairInterface) Class.forName(className).newInstance();
            } catch (InstantiationException | IllegalAccessException
                    | ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return hair;
        }
        //第三种:根据类名来生成对象(通过读取配置文件,找到相应的类名进行实例化,这样在增加类的时候大大简化了操作)
        public HairInterface getHairByKey(String Key){
            HairInterface hair = null;
            try {

                Map<String,String> map = new PropertiesReader().getProperties();
                hair = (HairInterface) Class.forName(map.get(Key)).newInstance();
            } catch (InstantiationException | IllegalAccessException
                    | ClassNotFoundException e) {
                e.printStackTrace();
            }
            return hair;
        }
    }

    /*
     * prrperties文件的读取工具类
     */
    class PropertiesReader {
        public Map<String, String> getProperties() {
            Properties props = new Properties();
            Map<String, String> map = new HashMap<String, String>();
            try {
                InputStream in = PropertiesReader.class
                        .getResourceAsStream("type.properties");
                props.load(in);
                Enumeration en = props.propertyNames();
                while (en.hasMoreElements()) {
                    String key = (String) en.nextElement();
                    String property = props.getProperty(key);
                    map.put(key, property);
                }
                return map;
            } catch (Exception e) {
                System.err.println("不能读取属性文件. "
                        + "请确保db.properties在CLASSPATH指定的路径中");
            }
            return null;
        }
    }

/*
 * 测试类  
 */
 class SunyTest {
    public static void main(String[] args) {
        HairFactory factory = new HairFactory();
        HairInterface hair = (HairInterface) factory.getHairByKey("right");
        hair.draw();
    }

}

配置文件type.properties

left=com.sunny.project.LeftHair
right=com.sunny.project.RightHair

实例总结:
该实例共有接口类、实体类、工厂类、工具类(提供从配置文件读取数据的方法)、测试类这5种类,其中工厂类提供了三种工厂方法实例化对象的方法,第三种方法在新添加一个实体类的时候修改的代码最少(只需要新建一个实体类然后在配置文件上加上该类就OK了)
抽象工厂模式

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值