集合

8.PNG

7 集合

今日学习内容

u Collection集合

u Iterator迭代器

u 增强for循环

u 泛型

今日学习目标

u 能够说出集合与数组的区别

u 说出Collection集合的常用功能

u 能够使用迭代器对集合进行取元素

u 能够说出集合的使用细节

u 能够使用集合存储自定义类型

u 能够使用foreach循环遍历集合

u 能够使用泛型定义集合对象

u 能够阐述泛型通配符的作用

第1章     集合

1.1      集合

1.1.1            集合介绍

前面的学习,我们知道数据多了,使用数组存放。而且数组中存放的都是基本类型的数据,并且数组是定长的。


当在程序中创建的对象比较多的时候,需要对这些对象进行统一的管理和操作,那么首先我们就需要把这些对象存储起来。使用数组是可以存放对象的,我们可以定义对象数组来存放,但是数组这个容器存放对象,要对其中的对象进行更复杂操作时,数据就显的很麻烦。那怎么办呢?

Java中给我们提供了另外一类容器,专门用来存放对象,这个容器就是我们要学习的集合。

集合和数组既然都是容器,它们有啥区别呢?

数组的长度是固定的。集合的长度是可变的。

数组中存储的是同一类型的元素,可以存储基本数据类型值。

集合存储的都是对象。而且对象的类型可以不一致。ArrayList<Object>

集合貌似看起来比较强大,它啥时用呢?


当对象多的时候,先进行存储。

1.1.2            集合框架的由来

集合本身是一个工具,它存放在java.util包中。

JDK最早的1.0版本中。提供的集合容器很少Vector。升级到1.2版,为了更多的需求,出现了集合框架。有了更多的容器(为什么提供那么多集合容器呢?)。可以完成不同的需求。

这些容器怎么区分?区分的方式:每一个容器的数据结构(数据存储一种方式)不一样。


例如:水缸和衣柜,饮料瓶等等.


不同的容器进行不断的向上抽取,最后形成了一个集合框架,这个框架就是Collection接口(一会可以看一下API)。在Collection接口定义着集合框架中最最最共性的内容。

在学习时:我们需要看最顶层怎么用, 创建底层对象即可。因为底层继承了父类中的所有功能。

1.1.3            接口的描述

既然Collection接口是集合中的顶层接口,那么它中定义的所有功能子类都可以使用。查阅API中描述的Collection接口。Collection 层次结构 中的根接口。Collection 用来存储一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。

一些 collection 是有序的,而另一些则是无序的。

其实我们在使用ArrayList类时,该类已经把所有抽象方法进行了重写。那么,实现Collection接口的所有子类都会进行方法重写。

l  Collecton接口常用的子接口有:List接口(列表)、Set接口(集)

l  List接口常用的子类有:ArrayList类、LinkedList类 

l  Set接口常用的子类有:HashSet类、LinkedHashSet类

9.PNG

继续查阅API,发现Collection接口中很多集合的操作方法,那么这些方法都具体能做什么呢?

1.1.4            Collection基本方法了解

1.2      Collection接口的基本方法

10.PNG

这里我们不关心具体创建的Collection中的那个子类对象,这里重点演示的是Collection接口中的方法

Collection<String> coll = new ArrayList<String>();

//1,往集合中添加对象元素。add(Object);

coll.add(“itcast1”);

coll.add(“itcast2”);

coll.add(“itcast3”);       

//2,删除。

coll.remove(“itcast2”);    

//3,判断是否包含。

System.out.println(coll.contains(“itcast11”));     

//4,清除。

coll.clear();

//把集合打印一下。

System.out.println(coll);//[itcast1, itcast2, itcast3]


第2章     Iterator迭代器

2.1      Iterator迭代器概述

java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。

Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

我们来画图描述一下迭代器的作用.

2.2      Iterator迭代方式的代码体现


Collection接口描述了一个抽象方法iterator方法,所有Collection子类都实现了这个方法,并且有自己的迭代形式。

11.PNG

集合中把这种取元素的方式描述在Iterator接口中。Iterator接口的常用方法如下:

12.PNG

l  hasNext()方法:用来判断集合中是否有下一个元素可以迭代。如果返回true,说明可以迭代。

l  next()方法:用来返回迭代的下一个元素,并把指针向后移动一位。


进行代码演示:

//1,创建集合对象。

Collection<String> coll = new ArrayList<String>();

coll.add(“abc1”);

coll.add(“abc2”);

coll.add(“abc3”);

coll.add(“abc4”);


//2.获取容器的迭代器对象。通过iterator方法。

Iterator it = coll.iterator();


//3,使用具体的迭代器对象获取集合中的元素。参阅迭代器的方法

while(it.hasNext()){

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

}


/

迭代器for循环的形式的使用

for (Iterator it = coll.iterator(); it.hasNext();  ) {

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

}

/

注意:在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。


迭代集合元素图解:

13.PNG


下边分别介绍以上内容:

a)     Collection接口的iterator

         方法声明为:

Iterator<集合中数据类型> iterator()

         用来返回专属于该集合对象的迭代器对象(Iterator的子类对象)

b)     Iterator接口

该接口规定了迭代集合所需要的方法

c)      Iterator接口的两个方法:hasNextnext方法

Iterator规定了两个方法,集合对象产生的迭代器对象正是通过这两个方法帮助集合进行迭代工作的。

调用迭代器的hasNext方法判断是否有下一个元素

调用迭代器的next获取下一个元素


2.3      并发修改异常

举例:

使用Iterator对象或者增强for循环遍历集合,如果出现”itcast”字符串,那么向集合中添加一个大写的”ITCAST”字符串


迭代的常规用法中我们要尽量避免在迭代过程中为集合添加/删除数据。否则会报错,原因是Java抛出了并发修改异常。        

迭代过程中并发修改异常的原因为:

迭代器中记忆的集合长度与集合中实际长度不同,而导致出现索引与实际元素不符甚至无限循环的情况发生。

所以在使用Iterator时,避免类似操作,for循环底层为迭代器实现,所以也需要避免类似操作。

有些迭代器避免了这样的问题,如ListIterator,但该类并不通用也不常用,实际开发中很少使用,只需要简单了解。


java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。

2.4      增强for循环

增强for循环是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。

格式:

for(元素的数据类型  变量 : Collection集合or数组){

}

它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。

练习一:遍历数组

int[] arr = new int[]{11,22,33};

for (int n : arr) {//变量n代表被遍历到的数组元素

    System.out.println(n);

}


练习二:遍历集合

Collection<String> coll = new ArrayList<String>();

coll.add(“itcast1”);

coll.add(“itcast2”);

coll.add(“itcast3”);

coll.add(“itcast4”);

for(String str : coll){//变量Str代表被遍历到的集合元素

    System.out.println(str);

}

增强for循环和老式的for循环有什么区别?

注意:新for循环必须有被遍历的目标。目标只能是Collection或者是数组。

建议:遍历数组时,如果仅为遍历,可以使用增强for如果要对数组的元素进行 操作,使用老式for循环可以通过角标操作。

第3章     泛型

3.1      泛型概述

泛型:泛泛的类型,就是一种不确定的类型(JDK1.5的一个新特性)

基本体现: <E>这就是泛型,此处的E是什么数据类型?


泛型用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数传递。

泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。

泛型的定义:定义泛型可以在类中预支地使用未知的类型。

泛型的使用:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

3.2      使用泛型的好处

l  将运行时期的ClassCastException,转移到了编译时期变成了编译失败。

l  避免了类型强转的麻烦。

演示下列代码:

publicclass GenericDemo {

     /

       使用集合对象存储并遍历

       不使用泛型 <> 不写

     /

    public static void demo01(){

        //创建集合对象,不使用泛型

        //没有明确集合存储的数据类型是什么, 什么都能存 Object

        ArrayList array = new ArrayList();

        array.add(“abc”);

        array.add(123);

       

        Iterator it = array.iterator();

        while(it.hasNext()){

            //迭代器的方法next()返回值是什么 Object

            //System.out.println(it.next());

            String s = (String)it.next();

            System.out.println(s);

        }

    }

     /

      使用泛型

      /

    publicstaticvoid demo02() {

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

        list.add(“abc”);

        list.add(“itcast”);

        //list.add(5);//当集合明确类型后,存放类型不一致就会编译报错

        //集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型

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

        while(it.hasNext()){

    String str = it.next();

//当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型

            System.out.println(str.length());

        }

    }

}


3.3      泛型的定义与使用


我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。

泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。

3.3.1            泛型中E的含义

我们以ArrayList<E>为例:

类名:ArrayList<E>

方法:

boolean add(E e);

E get(int index)

画图说明:

14.PNG

3.3.2            含有泛型的类:泛型类

         定义格式:修饰符 class 类名<代表泛型的变量> {  }

n  例如,API中的ArrayList集合:

class ArrayList<E>{

public boolean add(E e){ }

    public Eget(int index){  }

}


         使用格式:创建对象时,确定泛型的类型

n  例如,ArrayList<String> list = new ArrayList<String>();

此时,变量E的值就是String类型

class ArrayList<String>{

public boolean add(String e){ }

    public Stringget(int index){  }

}

n  例如,ArrayList<Integer> list = new ArrayList<Integer>();

此时,变量E的值就是Integer类型

class ArrayList<Integer>{

public boolean add(Integer e){ }

    public Integerget(int index){  }

}

举例自定义泛型类

publicclass GenericClass<E>{//自定义的类中,可以写<>泛型

   //E 表示未知的数据类型 调用者创建对象的时候,才能明确数据类型

    private E e;

   

    publicvoid setE(E e){

        this.e = e;

    }

   

    public E getE(){

        returne;

    }

}

使用:

publicclass GenericClassTest {

    publicstaticvoid main(String[] args) {

        //对自定义的泛型类,进行测试

        GenericClass<Integer> g = new GenericClass<Integer>(); 

         //E传递什么类型就是什么类型

        g.setE(100);   

        Integer i = g.getE();  

        System.out.println(i);

    }

}

3.3.3            含有泛型的方法

定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }

例如,

publicclass GenericMethod <E>{

    publicvoid show1(E e){

        System.out.println(e);

    }

   

    public <T> void show2(T t){//自定义泛型的方法

//自己写一个方法,方法中的数据类型,采用<>泛型

    //如果方法中的泛型,和类上的泛型不同

// 在方法返回值前加入<>

        System.out.println(t);

    }

}

使用格式:调用方法时,确定泛型的类型

publicclass GenericMethodTest {

    publicstaticvoid main(String[] args) {

        GenericMethod<Double> g = new GenericMethod<Double>();

        g.show(1.1);

       

        g.function(1.2F);//传递什么类型就是什么类型

    }

}

3.3.4            含有泛型的接口

         定义格式:修饰符 interface接口名<代表泛型的变量> {  }

n  例如,

publicinterface Inter <E>{

    publicabstractvoid show(E e);

}

        

使用格式:

1、实现接口时,确定泛型的类型

n  例如

publicclass InterImpl implements Inter<Integer>{

    publicvoid show(Integer i){

        System.out.println(i);

    }

}

此时,变量E的值就是Integer类型。


2、实现接口,不指定泛型的类型,直到创建对象时,确定泛型的类型

n  例如

InterImpl<String> imp= new InterImpl<String>();

此时,变量E的值就是String类型。

publicclass InterImpl<E>implements Inter<E>{

    publicvoid show(E e){

        System.out.println(e);

    }

}

3.4      泛型通配符

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

定义:(查看ArrayList的构造方法或者addAll方法)无法在类中使用

            使用:调用方法时可以给予任意类型。参照Arraylist的构造方法或者addAll方法

? extends E代表只要是E类型的子类即可

? super E代表只要是E类型的父类即可


/

  泛型通配符?,代表任意的数据类型

 

  定义:(查看ArrayList的addAll)无法在类中使用

 

  使用:调用方法时可以给予任意类型。参照Arraylistadd方法

  public boolean addAll(Collection<? extends E> c)

  为了便于?的理解,我们将以上方法重写为public boolean addAll(ArrayList<? extends E> c)

 

  该方法的意思:集合对象A调用addAll方法,一个集合对象B作为参数,即向集合A中添加集合B中的元素

 

  ? extends E代表只要是E类型的子类即可

  ? super E代表只要是E类型的父类即可

 */

publicclass Demo01 {

    publicstaticvoid main(String[] args) {     

        //定义集合b,包含3个元素

        ArrayList<String> listB = new ArrayList<String>();

        listB.add(“Jack”);

        listB.add(“Rose”);

        listB.add(“Trump”);

       

        //使用集合b创建集合a

        ArrayList<Object> listA = new ArrayList<Object>();

        listA.add(“Obama”);

       

         listA.addAll(listB);

        //观察集合A

        System.out.println(listA);

    }


第4章     集合综合案例

4.1      案例介绍

按照斗地主的规则,完成洗牌发牌的动作。

具体规则:

         使用54张牌

打乱顺序

         三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

4.2      案例需求分析

l  准备牌:

牌可以设计为一个ArrayList<String>,每个字符串为一张牌。

每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。

牌由Collections类的shuffle方法进行随机排序。

l  发牌:

将每个人以及底牌设计为ArrayList<String>,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。

l  看牌:

直接打印每个集合。


4.3      实现代码步骤

15.PNG

修改文件编码由GBK修改为UTF-8,因为GBK没有我们要的梅花、方片、黑桃、红桃(♠♥♦♣)等字符。

publicclass Poker {


    publicstaticvoid main(String[] args) {


        //♠♥♦♣

        //准备牌

        ArrayList<String> poker = new ArrayList<String>();

        //花色

        ArrayList<String> color = new ArrayList<String>();

        color.add(“♠”);

        color.add(“♥”);

        color.add(“♦”);

        color.add(“♣”);

        //数字

        ArrayList<String> number = new ArrayList<String>();

        for (int i = 2; i <= 10; i++) {

            number.add(i+“”);

        }

        number.add(“J”);

        number.add(“Q”);

        number.add(“K”);

number.add(“A”);

        //完成新牌

        for (String thisColor : color) {

            for (String thisNumber : number) {

                String thisCard = thisColor + thisNumber;

                poker.add(thisCard);

            }

        }

        poker.add(☺”);

        poker.add(☻”);

        //洗牌

        Collections.shuffle(poker);

       

        //发牌

        //玩家1

        ArrayList<String> player1 = new ArrayList<String>();   

        //玩家2

        ArrayList<String> player2 = new ArrayList<String>();

        //玩家3

        ArrayList<String> player3 = new ArrayList<String>();

        //底牌

        ArrayList<String> secretCards = new ArrayList<String>();

       

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

            if(i>=51) {

                //最后三张发给底牌

                secretCards.add(poker.get(i));

            }else {

                //剩余牌通过对3取模依次摸牌

                if(i%3==0) {

                    player1.add(poker.get(i));

                }elseif(i%3==1) {

                    player2.add(poker.get(i));

                }else {

                    player3.add(poker.get(i));

                }

            }

        }

       

        //看牌

        System.out.println(player1);

        System.out.println(player2);

        System.out.println(player3);

        System.out.println(secretCards);

    }

}

l  最后发到三个人手中的牌是无序的,在明天学习完Map集合后,我们提供一个排序的解决方案。




常用API

第6天常用API

今日内容介绍

u 正则表达式

u Date

u DateFormat

u Calendar

u 基本类型包装类

u System

u Math

今日学习目标

u 能够理解正则表达式验证11位手机号码

u 能够理解正则表达式验证QQ号码

u 能够使用日期类输出当前的日期

u 能够说出将日期格式化成字符串的方法

u 说出将字符串转换成日期的方法

u 写出基本数据类型对应的八种包装类

u 写出字符串转换成基本数据类型的方法

u 写出基本数据类型转换成字符串方式

u 能够说出拆箱装箱概念

u 能够掌握System类常见方法的使用

u 能够使用Math类进行数学运算
第1章 Date
1.1 Date类概述

类 Date 表示特定的瞬间,精确到毫秒。

继续查阅Date类的描述,发现Date拥有多个构造函数,只是部分已经过时,但是其中有未过时的构造函数可以把毫秒值转成日期对象。

//创建日期对象,把当前的毫秒值转成日期对象

Date date = new Date(1607616000000L);

System.out.println(date);

//打印结果:Fri Dec 11 00:00:00 CST 2020

可是将毫秒值转成日期后,输出的格式不利于我们阅读,继续查阅API,Date中有getYear、getMouth等方法,可以他们已经过时,继续往下查阅,看到了toString方法。

点开toString()方法查阅,原来上面打印的date对象就是默认调用了这个toString方法.
1.2 Date类常用方法


l 把日期对象转换成对应的时间毫秒值

l 毫秒值是不断变化的,所以每次打印的不一样,金钱买不了时间
第2章 DateFormat
2.1 DateFormat类概述

DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat类)允许进行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和标准化。

我们通过这个类可以帮我们完成日期和文本之间的转换。

继续阅读API,DateFormat 可帮助进行格式化并解析任何语言环境的日期。对于月、星期,甚至日历格式(阴历和阳历),其代码可完全与语言环境的约定无关。
2.2 日期格式

要格式化一个当前语言环境下的日期也就是日期 -> 文本),要通过下面的方法来完成。DateFormat是抽象类,我们需要使用其子类SimpleDateFormat来创建对象。

l 构造方法

l DateFormat类方法

代码演示:

//创建日期格式化对象,在获取格式化对象时可以指定风格

DateFormat df= new SimpleDateFormat(“yyyy-MM-dd”);//对日期进行格式化

Date date = new Date(1607616000000L);

String str_time = df.format(date);

System.out.println(str_time);//2020年12月11日

l DateFormat类的作用:即可以将一个Date对象转换为一个符合指定格式的字符串,也可以将一个符合指定格式的字符串转为一个Date对象。

指定格式的具体规则我们可参照SimpleDateFormat类的说明,这里做简单介绍,规则是在一个字符串中,会将以下字母替换成对应时间组成部分,剩余内容原样输出:

l 当出现y时,会将y替换成年

l 当出现M时,会将M替换成月

l 当出现d时,会将d替换成日

l 当出现H时,会将H替换成时

l 当出现m时,会将m替换成分

l 当出现s时,会将s替换成秒
2.3 DateFormat类常用方法

l format方法,用来将Date对象转换成String

l parse方法,用来将String转换成Date(转换时,该String要符合指定格式,否则不能转换)。

代码演示:

练习一:把Date对象转换成String

 Date date = new Date(1607616000000L);//Fri Dec 11 00:00:00 CST 2020

DateFormat df = new SimpleDateFormat(“yyyy年MM月dd日”);

String str = df.format(date);

//str中的内容为2020年12月11日

练习二:把String转换成Date对象

String str = ”2020年12月11日”;

DateFormat df = new SimpleDateFormat(“yyyy年MM月dd日”);

Date date = df.parse( str );

//Date对象中的内容为Fri Dec 11 00:00:00 CST 2020

第3章 Calendar
3.1 Calendar类概念

Calendar是日历类,在Date后出现,替换掉了许多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量,方便获取。

Calendar为抽象类,由于语言敏感性,Calendar类在创建对象时并非直接创建,而是通过静态方法创建,将语言敏感内容处理好,再返回子类对象,如下:

l Calendar类静态方法

Calendar c = Calendar.getInstance(); //返回当前日历对象

Syso(c);//我们可以打印出来 看一下,东西很多不必要挨个研究

public static Calendar getInstance(){

Calendar c = new GregorianCalendar();

return c;

}

3.2 Calendar类常用方法

l public static Calendar getInstance() //获取日期对象

l public int get(int field) //获取时间字段值,字段参见帮助文档

n YEAR 年

n MONTH 月,从0开始算起,最大11;0代表1月,11代表12月。

n DATE 天

n HOUR 时

n MINUTE分

n SECOND秒

代码演示:

Calendar c = Calendar.getInstance();

int year = c.get(Calendar.YEAR);

//做一个小练习 打印当前时间:xxxx年xx月xx日(注意月份的时间???)

l public final void set(int field,int value)//设置指定字段的值

代码演示:

Calendar c = Calendar.getInstance();

//设置时间为2020年5月20日

c.set(Calendar.YEAR, 2020);

c.set(Calendar.MONTH, 4);

c.set(Calendar.DATE, 20);

l public final Date getTime() //获取该日历对象转成的日期对象

代码演示:

Calendar c = Calendar.getInstance();

Date d = c.getTime();

3.3 注意事项

西方星期的开始为周日,中国为周一。

在Calendar类中,月份的表示是以0-11代表1-12月。

日期是有大小关系的,时间靠后,时间越大。
第4章 System类
4.1 概念

在API中System类介绍的比较简单,我们给出定义,System中代表程序所在系统,提供了对应的一些系统属性信息,和系统操作。

查看文档System类不能手动创建对象,因为构造方法被private修饰,阻止外界创建对象。System类中的都是static方法,类名访问即可。在JDK中,有许多这样的类。
4.2 常用方法(arraycopy)

l currentTimeMillis() 获取当前系统时间与1970年01月01日00:00点之间的毫秒差值

l exit(int status) 用来结束正在运行的Java程序。参数传入一个数字即可。通常传入0记为正常状态,其他为异常状态

l gc() 用来运行JVM中的垃圾回收器,完成内存中垃圾的清除。

l getProperty(String key) 用来获取指定键(字符串名称)中所记录的系统属性信息

l

l
第5章 Math类
5.1 概念

Math 类是包含用于执行基本数学运算的方法的数学工具类,如初等指数、对数、平方根和三角函数。

类似这样的工具类,其所有方法均为静态方法,并且一般不会创建对象。如System类

5.2 常用方法

l abs方法,结果都为正数

double d1 = Math.abs(-5); // d1的值为5

double d2 = Math.abs(5); // d2的值为5

l ceil方法,结果为比参数值大的最小整数的double值

double d1 = Math.ceil(3.3); //d1的值为 4.0

double d2 = Math.ceil(-3.3); //d2的值为 -3.0

double d3 = Math.ceil(5.1); // d3的值为 6.0

l floor方法,结果为比参数值小的最大整数的double值

double d1 = Math.floor(3.3); //d1的值为3.0

double d2 = Math.floor(-3.3); //d2的值为-4.0

double d3 = Math.floor(5.1); //d3的值为 5.0

l max方法,返回两个参数值中较大的值

double d1 = Math.max(3.3, 5.5); //d1的值为5.5

double d2 = Math.max(-3.3, -5.5); //d2的值为-3.3

l min方法,返回两个参数值中较小的值

double d1 = Math.min(3.3, 5.5); //d1的值为3.3

double d2 = Math.max(-3.3, -5.5); //d2的值为-5.5

l pow方法,返回第一个参数的第二个参数次幂的值

double d1 = Math.pow(2.0, 3.0); //d1的值为 8.0

double d2 = Math.pow(3.0, 3.0); //d2的值为27.0

l round方法,返回参数值四舍五入的结果

double d1 = Math.round(5.5); //d1的值为6.0

double d2 = Math.round(5.4); //d2的值为5.0

l random方法,产生一个大于等于0.0且小于1.0的double小数

double d1 = Math.random();
第6章 基本类型包装类

大家回想下,在第二天我们学习Java中的基本数据类型时,说Java中有8种基本的数据类型,可是这些数据是基本数据,想对其进行复杂操作,变的很难。怎么办呢?
6.1 基本类型包装类概述

在实际程序使用中,程序界面上用户输入的数据都是以字符串类型进行存储的。而程序开发中,我们需要把字符串数据,根据需求转换成指定的基本数据类型,如年龄需要转换成int类型,考试成绩需要转换成double类型等。那么,想实现字符串与基本数据之间转换怎么办呢?

Java中提供了相应的对象来解决该问题,基本数据类型对象包装类:java将基本数据类型值封装成了对象。封装成对象有什么好处?可以提供更多的操作基本数值的功能。

8种基本类型对应的包装类如下:

其中需要注意int对应的是Integer,char对应的Character,其他6个都是基本类型首字母大写即可。

基本数据类型对象包装类特点:用于在基本数据和字符串之间进行转换。

l 将字符串转成基本类型:

parseXXX(String s);其中XXX表示基本类型,参数为可以转成基本类型的字符串,如果字符串无法转成基本类型,将会发生数字转换的问题NumberFormatException

System.out.println(Integer.parseInt(“123”) + 2);

//打印结果为 125

基本类型变成String

基本类型+”” 就可以
6.2 自动装箱拆箱

在需要的情况下,基本类型与包装类型可以通用。有些时候我们必须使用引用数据类型时,可以传入基本数据类型。

比如:

基本类型可以使用运算符直接进行计算,但是引用类型不可以。而基本类型包装类作为引用类型的一种却可以计算,原因在于,Java”偷偷地”自动地进行了对象向基本数据类型的转换。

相对应的,引用数据类型变量的值必须是new出来的内存空间地址值,而我们可以将一个基本类型的值赋值给一个基本类型包装类的引用。原因同样在于Java又”偷偷地”自动地进行了基本数据类型向对象的转换。

l 自动拆箱:对象转成基本数值

l 自动装箱:基本数值转成对象

Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);

i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5; 加法运算完成后,再次装箱,把基本数值转成对象。
第7章 正则表达式
7.1 正则表达式的概念

正则表达式(英语:Regular Expression,在代码中常简写为regex)。

正则表达式是一个字符串,使用单个字符串来描述、用来定义匹配规则,匹配一系列符合某个句法规则的字符串。在开发中,正则表达式通常被用来检索、替换那些符合某个规则的文本。
7.2 字符串类中涉及正则表达式的常用方法

l public boolean matches(String regex) //判断字符串是否匹配给定的规则

举例:校验qq号码.

1:要求必须是5-15位数字

2:0不能开头

代码演示:

String qq = "604154942";

String regex = "[1-9][0-9]{4,14}";

booleanflag2 = qq.matches(regex);

举例:校验手机号码

1:要求为11位数字

2:第1位为1,第2位为3、4、5、7、8中的一个,后面9位为0到9之间的任意数字。

代码演示:

String phone = "18800022116";

String regex = "1[34578][0-9]{9}";

booleanflag = phone.matches(regex);

l public String[] split(String regex) //根据给定正则表达式的匹配规则,拆分此字符串

举例:分割出电话号码字符串中的的数字

代码演示:

String s = “18-22-40-65”;

String regex = "-";

String[] result = s.split(regex);

代码演示:

String s = "18 22 40 65";

String regex = " ";

String[] result = s.split(regex);

附录(这部分内容大家知道有就可以了):正则表达式的匹配规则

参照帮助文档,在Pattern类中有正则表达式的的规则定义,正则表达式中明确区分大小写字母。我们来学习语法规则。

正则表达式的语法规则:

字符:x

含义:代表的是字符x

例如:匹配规则为 “a”,那么需要匹配的字符串内容就是 ”a”

字符:\

含义:代表的是斜线字符’\’

例如:匹配规则为”\“ ,那么需要匹配的字符串内容就是 ”\”

字符:\t

含义:制表符

例如:匹配规则为”\t” ,那么对应的效果就是产生一个制表符的空间

字符:\n

含义:换行符

例如:匹配规则为”\n”,那么对应的效果就是换行,光标在原有位置的下一行

字符:\r

含义:回车符

例如:匹配规则为”\r”,那么对应的效果就是回车后的效果,光标来到下一行行首

字符类:[abc]

含义:代表的是字符a、b 或 c

例如:匹配规则为”[abc]”,那么需要匹配的内容就是字符a,或者字符b,或字符c的一个

字符类:[^abc]

含义:代表的是除了 a、b 或 c以外的任何字符

例如:匹配规则为”[^abc]”,那么需要匹配的内容就是不是字符a,或者不是字符b,或不是字符c的任意一个字符

字符类:[a-zA-Z]

含义:代表的是a 到 z 或 A 到 Z,两头的字母包括在内

例如:匹配规则为”[a-zA-Z]”,那么需要匹配的是一个大写或者小写字母

字符类:[0-9]

含义:代表的是 0到9数字,两头的数字包括在内

例如:匹配规则为”[0-9]”,那么需要匹配的是一个数字

字符类:[a-zA-Z_0-9]

含义:代表的字母或者数字或者下划线(即单词字符)

例如:匹配规则为” [a-zA-Z_0-9] “,那么需要匹配的是一个字母或者是一个数字或一个下滑线

预定义字符类:.

含义:代表的是任何字符

例如:匹配规则为” . “,那么需要匹配的是一个任意字符。如果,就想使用 . 的话,使用匹配规则”\.”来实现

s

预定义字符类:\d

含义:代表的是 0到9数字,两头的数字包括在内,相当于[0-9]

例如:匹配规则为”\d “,那么需要匹配的是一个数字

预定义字符类:\w

含义:代表的字母或者数字或者下划线(即单词字符),相当于[a-zA-Z_0-9]

例如:匹配规则为”\w “,,那么需要匹配的是一个字母或者是一个数字或一个下滑线

边界匹配器:^

含义:代表的是行的开头

例如:匹配规则为^[abc][0-9]$ ,那么需要匹配的内容从[abc]这个位置开始, 相当于左双引号

边界匹配器:$

含义:代表的是行的结尾

例如:匹配规则为^[abc][0-9]$ ,那么需要匹配的内容以[0-9]这个结束, 相当于右双引号

边界匹配器:\b

含义:代表的是单词边界

例如:匹配规则为”\b[abc]\b”,那么代表的是字母a或b或c的左右两边需要的是非单词字符([a-zA-Z_0-9])

String regex = ".*\\b[abc]\\b.*";

String str = "abc a bbc";

booleanflag = str.matches(regex);

数量词:X?

含义:代表的是X出现一次或一次也没有

例如:匹配规则为”a?”,那么需要匹配的内容是一个字符a,或者一个a都没有

数量词:X*

含义:代表的是X出现零次或多次

例如:匹配规则为”a*”,那么需要匹配的内容是多个字符a,或者一个a都没有

数量词:X+

含义:代表的是X出现一次或多次

例如:匹配规则为”a+”,那么需要匹配的内容是多个字符a,或者一个a

数量词:X{n}

含义:代表的是X出现恰好 n 次

例如:匹配规则为”a{5}”,那么需要匹配的内容是5个字符a

数量词:X{n,}

含义:代表的是X出现至少 n 次

例如:匹配规则为”a{5, }”,那么需要匹配的内容是最少有5个字符a

数量词:X{n,m}

含义:代表的是X出现至少 n 次,但是不超过 m 次

例如:匹配规则为”a{5,8}”,那么需要匹配的内容是有5个字符a 到 8个字符a之间

逻辑运算符:XY

含义:代表的是X后跟Y

例如:匹配规则为”ab”,那么需要匹配的字符串内容就是 ”ab”

逻辑运算符:X|Y

含义:代表的是X 或 Y

例如:匹配规则为”a|b”,那么需要匹配的字符串内容就是 ”a”或”b”

逻辑运算符:(X)

含义:代表的是()括号内的数据作为一组数据出现,(X)的方式称为正则表达式中的组

例如:匹配规则为”(hello)+”,那么需要匹配的内容是组内数据要出现多次,如”hellohellohello”;或者组内数据出现一次,如”hello”。

l 注意实现:

n 在定义正则表达式的匹配规则字符串的里面,想再次使用组中的内容,可通过\1来进行使用

例如:正则表达式的匹配规则为”(a) == \1”;

使用数据”a == a”进行匹配结果为true;使用数据”a == b”进行匹配结果为false。

重写、this、super,抽象类

继承
1.1 继承中子类中出现与父类重名的成员变量

当子父类中出现了同名成员变量时,在子类中若要访问父类中的成员变量,必须使用关键字super来完成。

在子类中,访问父类中的成员变量格式:

super.父类中的成员变量

看如下代码:
class Fu
{
//Fu中的成员变量。
int num = 5;
}
class Zi extends Fu
{
//Zi中的成员变量
int num = 6;
void show()
{
//子父类中出现了同名的成员变量时
//在子类中需要访问父类中非私有成员变量时,需要使用super关键字
//访问父类中的num
System.out.println(“Fu num=”+super.num);
//访问子类中的num2
System.out.println(“Zi num2=”+this.num);
}
}
class Demo5
{
public static void main(String[] args)
{
Zi z = new Zi(); //创建子类对象
z.show(); //调用子类中的show方法
}
}

1.2 继承后子类对象的内存图

当子类创建对象后,该子类对象本身可以使用this来指代,而该对象当中的父类空间可以使用super来指代。

如下为加入了子父类关系后的对象内存图。

继承后对象内存图

1.3 继承中方法重写&应用

 子父类成员方法特殊情况——覆盖

子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为override重写、复写或者覆盖。

class Fu
{
public void show()
{
    System.out.println("Fu show");
}
}
class Zi extends Fu
{
//子类复写了父类的show方法
public void show()
{
    System.out.println("Zi show");
}
}

方法重写(覆盖)的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

举例:比如手机,当描述一个手机时,它具有发短信,打电话,显示来电号码功能,后期由于手机需要在来电显示功能中增加显示姓名和头像,这时可以重新定义一个类描述智能手机,并继承原有描述手机的类。并在新定义的类中覆盖来电显示功能,在其中增加显示姓名和头像功能。

在子类中,访问父类中的成员方法格式:
super.父类中的成员方法();

看如下代码:

public class Test {
public static void main(String[] args) {
    new NewPhone().showNum();
}
}

//手机类
class Phone{
public void sendMessage(){
    System.out.println("发短信");
}
public void call(){
    System.out.println("打电话");
}
public void showNum(){
    System.out.println("来电显示号码");
}
}

//智能手机类
class NewPhone extends Phone{

//覆盖父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
    //调用父类已经存在的功能使用super
    super.showNum();
    //增加自己特有显示姓名和图片功能
    System.out.println("显示来电姓名");
    System.out.println("显示头像");
}
}

1.4 方法重写的注意事项

重写需要注意的细节问题:

 子类方法覆盖/重写/覆写(override)父类方法,必须要保证权限大于等于父类权限。

class Fu(){    
void show(){}
    public void method(){}
}
class Zi() extends Fu{
public void show(){}  //编译运行没问题
    void method(){}      //编译错误
}

 如果父类的方法是private的修饰的,那么在子类中没法重写(其他修饰符都可以重写)

 写法上稍微注意:必须一模一样:方法的返回值类型 方法名 参数列表都要一样。

总结:当一个类是另一个类中的一种时,可以通过继承,来继承属性与功能。如果父类具备的功能内容需要子类特殊定义时,进行方法重写。

1.5 this与super调用普通成员与构造方法

调用普通成员:
this.成员变量       可以访问本类对象的成员变量
super.成员变量     可以访问父类对象的成员变量

this.成员方法()     可以访问本类对象的成员方法
super.成员方法()    可以访问父类对象的成员方法

使用this找本类,如果子类没有找父类
使用super找父类,如果父类没有不会去找子类

调用构造方法:
this(其他参数)      可以访问本类其他的构造方法
super(其他参数)    可以访问父类其他的构造方法

   默认子类调用父类构造方法
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super();

1.6 [扩展1]使用this调用其他构造方法

public class Person {
private int age;
public Person(){
    this(15);//这句话调用 下面带int参数构造
}
public Person(int age){
    this.age = age;
}
public int getAge() {
    return age;
}
public void setAge(int age) {
    this.age = age;
}
}
/*
 *  以前this.这种形式;
 *  现在:this() 语句形式
 *  调用本类中的其他构造方法
 *  注意:this()语句形式必须写在构造方法的第一行
 */
public class Test {
public static void main(String[] args) {
    Person p = new Person();
    int a = p.getAge();
    System.out.println(a);

/*    Person p1 = new Person(15);
    a = p1.getAge();
    System.out.println(a);*/
}
}

需求1,创建一个人的对象,要求:无论通过何种构造方法创建,人对象的年龄就是10岁.

1.7 [扩展2]使用super调用父类的构造方法

一.子类的每个每个每个构造方法中均有默认的super(),调用父类的空参构造

public class Fu {

    public Fu(){
        System.out.println("父类的构造方法");
    }
}

public class Zi extends Fu{
int a;
    public Zi(){
        //super();
        System.out.println("子类的构造方法");
    }
public Zi(int a){
        this.a = a;
        System.out.println("子类的构造方法");
    }


}
public class Test {
    public static void main(String[] args) {
        new Zi();//匿名对象
}
}

二. super()调用的是父类无参的构造方法 但是能不能调用父类其他有参构造呢?

public class Fu {
int a;
    public Fu(){
        System.out.println("父类的构造方法");
    }
public Fu(int a){
        this.a = a;
    }

}

public class Zi extends Fu{
    public Zi(){
           System.out.println("子类的构造方法");
    }
}
public class Test {
    public static void main(String[] args) {
        new Zi();//匿名对象
}
}

思考:
Java设计的时候为什么这么干????
原因[了解]:
Java设计的时候坚持了一个原则,谁污染谁治理,谁的孩子谁负责
谁的成员变量 谁初始化(类不能太累,单一职责原则)

18.this与super

18.1父类对象空间优于子类对象产生

在每次创建子类对象时,我们均会先创建父类对象,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类对象空间,便可以包含其父类对象的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。

代码体现在子类的构造方法调用时,一定先调用父类的构造方法。

18.2继承关系图(this与super)

当子类创建对象后,该子类对象本身可以使用this来指代,而该对象当中的父类对象空间可以使用super来指代。

如下为加入了子父类关系后的对象内存图。

以Person类为例:

//定义父类
public class Person {
    private String name;
    private int age;

    public Person(){}
    public Person(String name,int age) {
        this.name = name;
        this.age = age;
     }
    //get/set方法
 }

//定义子类
public class Chinese extends Person{
private String address;
public Chinese(){}
public Chinese(String name,int age,String address) {
    super(name,age);
    this.address = address;
}
//对address的get/set
}


//定义测试类,使用子类创建对象
public class Test{
Chinese c = new Chinese(“AngelaBaby”,18,”北京海淀区上地软件园”);
}

继承后对象内存图

第2章 抽象类

2.1 抽象类-产生

当编写一个类时,我们往往会为该类定义一些方法,这些方法是用来描述该类的功能具体实现方式,那么这些方法都有具体的方法体。

但是有的时候,某个父类只是知道子类应该包含怎么样的方法,但是无法准确知道子类如何实现这些方法。

比如一个图形类应该有一个求周长的方法,但是不同的图形求周长的算法不一样。那该怎么办呢?

再比如:动物应该有吃饭的方法,猫,狗,猪…. 那么在他们的父类中 这个吃方法应该怎么定义?

分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是方法功能声明相同,但方法功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法。

如:
描述讲师的行为:工作。
描述助教的行为:工作。
描述班主任的行为:工作。
讲师、助教、班主任之间有共性,可以进行向上抽取。抽取它们的所属共性类型:员工。由于讲师、助教、班主任都具有工作功能,但是他们具体工作内容却不一样。这时在描述员工时,发现了有些功能不能够具体描述,那么,这些不具体的功能,需要在类中标识出来,通过java中的关键字abstract(抽象)修饰。

当定义了抽象函数的类也必须被abstract关键字修饰,被abstract关键字修饰的类是抽象类。

2.2 抽象类&抽象方法的定义

抽象方法定义的格式:

public abstract 返回值类型 方法名(参数);

抽象类定义的格式:

abstract class 类名 {
}

看如下代码:

//员工
abstractclass Employee{
    public abstract void work();//抽象函数。需要abstract修饰,并分号;结束
}

//讲师
class Teacher extends Employee {
    publicvoid work() {
        System.out.println("正在讲解Java");
    }
}

//助教
class Assistant extends Employee {
    publicvoid work() {
        System.out.println("正在辅导学生");
    }
}

//班主任
class Manager extends Employee {
    publicvoid work() {
        System.out.println("正在管理班级");
    }
}

2.3 抽象类&抽象方法的使用

抽象类无法直接创建对象,只能被子类继承后,创建子类对象。

子类需要继承抽象父类并完成最终的方法实现细节(即重写方法,完成方法体)。而此时,方法重写不再是加强父类方法功能,而是父类没有具体实现,子类完成了具体实现,我们将这种方法重写也叫做实现方法。

抽象类是拥有构造方法的,其存在的意义在于对自身进行初始化,供其子类使用。

2.4 抽象类常见疑惑

 抽象类一定可以是个父类,因为抽象类时不断抽取共性需求而来的。

 抽象类中是可以不定义抽象方法的,此时仅仅是不让该类创建对象,用于某些特殊的设计需要。

 设计时由具体类抽取出抽象类,而开发阶段应该先定义抽象父类,再根据不同需求由父类定义子类。

第3章 综合案例—员工类系列定义

3.1 案例介绍

某IT公司有多名员工,按照员工负责的工作不同,进行了部门的划分(研发部员工、维护部员工)。研发部根据所需研发的内容不同,又分为JavaEE工程师、Android工程师;维护部根据所需维护的内容不同,又分为网络维护工程师、硬件维护工程师.

公司的每名员工都有他们自己的员工编号、姓名,并要做它们所负责的工作。

    工作内容
    JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
    Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
    网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
    硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机

请根据描述,完成员工体系中所有类的定义,并指定类之间的继承关系。进行XX工程师类的对象创建,完成工作方法的调用。

3.2 案例分析

 根据上述部门的描述,得出如下的员工体系图

 根据员工信息的描述,确定每个员工都有员工编号、姓名、要进行工作。则,把这些共同的属性与功能抽取到父类中(员工类),关于工作的内 容由具体的工程师来进行指定。

    工作内容
    JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
    Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
    网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
    硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机

 创建JavaEE工程师对象,完成工作方法的调用

3.3 案例代码实现

 根据员工体系图,完成类的定义

定义员工类(抽象类)

publicabstractclass Employee {
private String id;// 员工编号
private String name; // 员工姓名

public String getId() {
    returnid;
}
publicvoid setId(String id) {
    this.id = id;
}
public String getName() {
    returnname;
}
publicvoid setName(String name) {
    this.name = name;
}

//工作方法(抽象方法)
publicabstractvoid work();
}

 定义研发部员工类Developer 继承 员工类Employee

publicabstractclass Developer extends Employee {
}

 定义维护部员工类Maintainer 继承 员工类Employee

publicabstractclass Maintainer extends Employee {
}

 定义JavaEE工程师 继承 研发部员工类,重写工作方法

publicclass JavaEE extends Developer {
    @Override
    publicvoid work() {
        System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在研发淘宝网站");
    }
}

 定义Android工程师 继承 研发部员工类,重写工作方法

publicclass Android extends Developer {
    @Override
    publicvoid work() {
        System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在研发淘宝手机客户端软件");
    }
}

 定义Network网络维护工程师 继承 维护部员工类,重写工作方法

publicclass Network extends Maintainer {
@Override
publicvoid work() {
    System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在检查网络是否畅通");
}
}

 定义Hardware硬件维护工程师 继承 维护部员工类,重写工作方法

publicclass Hardware extends Maintainer {
@Override
publicvoid work() {
    System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在修复打印机");
}
}

 在测试类中,创建JavaEE工程师对象,完成工作方法的调用

publicclass Test {
publicstaticvoid main(String[] args) {
    //创建JavaEE工程师员工对象
    JavaEE ee = new JavaEE();
    //设置该员工的编号
    ee.setId("000015");
    //设置该员工的姓名
    ee.setName("小明");
    //调用该员工的工作方法
    ee.work();
}
}

面向对象

什么叫面向对象编程?有人是这么理解的

也有人说不对,面向对象编程是你要编程你对象也要编程.

面向对象简称:“OOP” 面向对象编程

使用类映射现实生活中的事物,其功能封装为方法,属性封装为成员变量。

一个类拥有的成员包括:

成员变量
构造方法
普通方法
getters/setters

如一个Person类的定义:

class Person{
private String name;
private int age;

public Person(){}
public Person(String name,int age) {
    this.name = name;
    this.age = age;
}

public void showName() {
    System.out.println(this.name);
}
set/get方法
}

1.3 对象的内存解释

对象在内存中的位置:
对象由new关键字创建,如同数组,实体存在于堆内存中

任何事物均可以定义成类,创建对象,属于引用类型

而对象的引用变量是一个普通变量。存储的值是该对象堆内存中的地址。

基本类型赋值代码解释

基本类型赋值对应内存图

引用类型赋值代码解释

引用类型赋值对应内存图

1.4 this关键字概念

this一般出现在类的一个方法当中,代表当前对象的引用。我们通常用其将本对象中的成员与其他作用域区分开来。
我们用代码回顾一下 this关键字的作用,用来区分成员变量和局部变量同名情况
当一个类定义好之后,如果不创建对象this是没有意义的。
一个类可以有多个对象。每个对象都有各自的属性值,各个对象的方法均是类中定义的那个方法逻辑。定义中的this就代表调用方法的这个对象。

1.5 匿名对象
1.5.1 匿名对象的概念
匿名: 不露身分、个人特征或不说明是什么人物
生活中有哪些匿名案例?
匿名账号

匿名举报

匿名对象

匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。

如:已经存在的类:
public class Person{
public void eat(){
System.out.println();
}
}

创建一个普通对象
Person p = new Person();
创建一个匿名对象
new Person();

1.5.2 匿名对象的特点
 创建匿名对象直接使用,没有变量名。

new Person().eat() //eat方法被一个没有名字的Person对象调用了。

 匿名对象在没有指定其引用变量时,只能使用一次。

new Person().eat(); 创建一个匿名对象,调用eat方法
new Person().eat(); 想再次调用eat方法,重新创建了一个匿名对象

 匿名对象可以作为方法接收的参数、方法返回值使用

class Demo {
publicstatic Person getPerson(){
    //普通方式
    //Person p = new Person();    
    //return p;

    //匿名对象作为方法返回值
    returnnew Person(); 
}

publicstaticvoid method(Person p){}
}

class Test {
publicstaticvoid main(String[] args) {
    //调用getPerson方法,得到一个Person对象
    Person person = Demo.getPerson();

    //调用method方法
    Demo.method(person);
    //匿名对象作为方法接收的参数
    Demo.method(new Person());
}
}

继承

2.1.1 继承的概念

什么叫继承? 他是面向对象的三大特征之一

现实生活中的继承 QQ:5位 12345

在现实生活中,继承一般指的是子女继承父辈的财产。

在程序中,继承描述的是类与类之间的关系,通过继承可以多个类之间形成一种关系体系。例如学校中的讲师、助教、班主任都属于员工。这些员工之间会形成一个继承体系,具体如下图所示。

图1-1 员工继承关系图
在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,
继承后子类的特点:
子类会自动拥有父类所有可继承的属性和方法。

思考:
现实生活中还有哪些继承的例子?

2.1.2 继承的定义及使用格式

在程序中,如果想声明一个类继承另一个类,需要使用extends关键字。

格式:
class 子类 extends 父类 {}
接下来通过一个案例来学习子类是如何继承父类的,如下所示。Example01.java

/*
 * 定义员工类Employee
 */
class Employee {
String name; // 定义name属性
// 定义员工的工作方法
publicvoid work() {
    System.out.println("尽心尽力地工作");
}
}

/*
 * 定义讲师类Teacher 继承 员工类Employee
 */
classTeacherextends Employee {
// 定义一个打印name的方法
publicvoid printName() {
    System.out.println("name=" + name);
}
}

/*
 * 定义测试类
 */
publicclass Example01 {
publicstaticvoid main(String[] args) {
    Teachert = newTeacher (); // 创建一个讲师类对象
    t.name = "小明"; // 为该员工类的name属性进行赋值
    t.printName(); // 调用该员工的printName()方法
    t.work(); // 调用Developer类继承来的work()方法
}
}

运行结果如下图所示。


图1-2 运行结果

在上述代码中,Teacher类通过extends关键字继承了Employee类,这样Teacher类便是Employee类的子类。从运行结果不难看出,子类虽然没有定义name属性和work()方法,但是却能访问这两个成员。这就说明,子类在继承父类的时候,会自动拥有父类的成员。

小结:
继承是面向对象的核心特性,是面向对象的学习重点。
继承是代码复用的重要方式,是类与类之间的一种关系。

继承的注意事项:

1.继承必须合理性:(人是人他妈生的 妖是妖他妈生的)
从类与类之间的设计关系来看,子类必须属于父类的一种时 is a,才会继承。

2.父类中成员是共性的内容
父类抽取出了共性的内容,子类可以在父类基础上扩展新的属性与行为。

3.子类自动拥有父类的成员并且可以直接使用非私有的父类成员。

2.2 继承-子类中访问父类成员变量特点

了解了继承给我们带来的好处,提高了代码的复用性。继承让类与类或者说对象与对象之间产生了关系。那么,当继承出现后,类的成员之间产生了那些变化呢?

类的成员重点学习成员变量、成员方法的变化。

成员变量:如果子类父类中出现不同名的成员变量,这时的访问是没有任何问题。

看如下代码:

class Fu
{
//Fu中的成员变量。
int num = 5;
}
class Zi extends Fu
{
//Zi中的成员变量
int num2 = 6;
//Zi中的成员方法
public void show()
{
    //访问父类中的num
    System.out.println("Fu num="+num);
    //访问子类中的num2
    System.out.println("Zi num2="+num2);
}
}
class Demo 
{
public static void main(String[] args) 
{
    Zi z = new Zi(); //创建子类对象
//System.out.println(z.num1);
    //z.show(); //调用子类中的show方法
}
}

代码说明:Fu类中的成员变量是非私有的,子类中可以直接访问,若Fu类中的成员变量私有了,子类是不能直接访问的。

2.3 继承-子类中访问父类成员方法特点

子父类中成员方法的特点

当在程序中通过对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

看如下代码:

class Fu{
public void show(){
    System.out.println("Fu类中的show方法执行");
}
}
class Zi extends Fu{
public void show2(){
    System.out.println("Zi类中的show2方法执行");
}
}
public  class Test{
public static void main(String[] args) {
    Zi z = new Zi();
    z.show(); //子类中没有show方法,但是可以找到父类方法去执行
    z.show2();
}
}

继承特点

1.Java只支持单继承:就一个子类 只能最多直接继承一个父类

2.Java支持多层继承:

3.父类定义了继承树中共性内容,子类定义了该类个性内容。

说明:在以后的开发过程中

我们学习了多态后,要结合多态,能使用父类时尽量使用父类,提高程序扩展性。

使用SpringBoot+Dubbo搭建一个简单的分布式服务

目录:

使用 SpringBoot+Dubbo 搭建一个简单分布式服务

实战之前,先来看几个重要的概念

    什么是分布式?

    什么是 Duboo?

    Dubbo 架构

    什么是 RPC?

    为什么要用 Dubbo?

开始实战 1 :zookeeper 环境安装搭建

    1. 下载

    2. 解压

    3. 进入zookeeper目录,创建data文件夹。

    4. 进入/zookeeper/conf目录下,复制zoo_sample.cfg,命名为zoo.cfg

    5. 修改配置文件

    6. 启动测试

开始实战 2 :实现服务接口 dubbo-interface

    1. dubbo-interface 项目创建

    2. 创建接口类

    3. 将项目打成 jar 包供其他项目使用

开始实战 3 :实现服务提供者 dubbo-provider

    1. dubbo-provider 项目创建

    2. pom 文件引入相关依赖

    3. 在 application.properties 配置文件中配置 dubbo 相关信息

    4. 实现接口

    5. 服务提供者启动类编写

开始实战 4 :实现服务消费者 dubbo-consumer

    4. 编写一个简单 Controller 调用远程服务

    5. 服务消费者启动类编写

    6. 测试效果

使用 SpringBoot+Dubbo 搭建一个简单分布式服务

实战之前,先来看几个重要的概念

开始实战之前,我们先来简单的了解一下这样几个概念:Dubbo、RPC、分布式、由于本文的目的是带大家使用SpringBoot+Dubbo 搭建一个简单的分布式服务,所以这些概念我只会简单给大家普及一下,不会做深入探究。

什么是分布式?

分布式或者说 SOA 分布式重要的就是面向服务,说简单的分布式就是我们把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。比如电商系统可以简单地拆分成订单系统、商品系统、登录系统等等。

我们可以使用 Dubbo作为分布式系统的桥梁,那么什么是 Dubbo 呢?

什么是 Duboo?

Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。简单来说 Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

Dubbo 目前已经有接近 23k 的 Star ,Dubbo的Github 地址:https://github.com/apache/incubator-dubbo。另外,在开源中国举行的2018年度最受欢迎中国开源软件这个活动的评选中,Dubbo 更是凭借其超高人气仅次于 vue.js 和 ECharts 获得第三名的好成绩。

Dubbo 是由阿里开源,后来加入了 Apache 。正式由于 Dubbo 的出现,才使得越来越多的公司开始使用以及接受分布式架构。

下面我们简单地来看一下 Dubbo 的架构,加深对 Dubbo 的理解。

Dubbo 架构

下面我们再来看看 Dubbo 的架构,我们后面会使用 zookeeper 作为注册中心,这也是 Dubbo 官方推荐的一种方式。

上述节点简单说明:

Provider 暴露服务的服务提供方

Consumer 调用远程服务的服务消费方

Registry 服务注册与发现的注册中心

Monitor 统计服务的调用次数和调用时间的监控中心

Container 服务运行容器

调用关系说明:

服务容器负责启动,加载,运行服务提供者。

服务提供者在启动时,向注册中心注册自己提供的服务。

服务消费者在启动时,向注册中心订阅自己所需的服务。

注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

我们在讲 Dubbo 的时候提到了 Dubbo 实际上是一款 RPC 框架,那么RPC 究竟是什么呢?相信看了下面我对 RPC 的介绍你就明白了!

什么是 RPC?

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务A,B部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好。 RPC 的出现就是为了解决这个问题。

为什么要用 Dubbo?

如果你要开发分布式程序,你也可以直接基于 HTTP 接口进行通信,但是为什么要用 Dubbo呢?

我觉得主要可以从 Dubbo 提供的下面四点特性来说为什么要用 Dubbo:

负载均衡——同一个服务部署在不同的机器时该调用那一台机器上的服务

服务调用链路生成——服务之间互相是如何调用的

服务访问压力以及时长统计——当前系统的压力主要在哪里,如何来扩容和优化

服务降级——某个服务挂掉之后调用备用服务

开始实战 1 :zookeeper 环境安装搭建

我使用的是 CentOS 7.4 阿里云服务器,注意:如果你也同样阿里云服务器必须配置一个安全组,不然你的应用程序会无法访问你的 zookeeper 服务器,这一点我在后面也提到了。

  1. 下载

通过 http://mirror.bit.edu.cn/apache/zookeeper/ 这个链接下载,然后上传到Linux上。(可以说那个 Xhell 附带的文件传输功能)

或者直接在Linux中使用 wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.12/zookeeper-3.4.12.tar.gz 命令下载(版本号 3.4.12 是我写这篇文章的时候最新的稳定版本,各位可以根据实际情况修改)

  1. 解压

解压完毕之后修改一下解压之后所得的文件夹名

mv zookeeper-3.4.12 zookeeper

删除 zookeeper 安装包

rm -rf zookeeper-3.4.12.tar.gz

3 进入zookeeper目录,创建data文件夹。

mkdir data

进入 data 文件夹 然后执行pwd命令,复制所得的当前目录位置(就是我用红色圈出来的文字)

4 进入/zookeeper/conf目录下,复制zoo_sample.cfg,命名为zoo.cfg

cp zoo_sample.cfg zoo.cfg

5 修改配置文件

使用 vim zoo.cfg 命令修改配置文件

vim 文件------>进入文件----->命令模式------>按i进入编辑模式----->编辑文件 ------->按Esc进入底行模式----->输入:wq/q! (输入wq代表写入内容并退出,即保存;输入q!代表强制退出不保存。)

修改配置文件中的 data 属性:

dataDir=/usr/local/zookeeper/data

  1. 启动测试

进入 /zookeeper/bin 目录然后执行下面的命令

./zkServer.sh start

执行 ./zkServer.sh status 查看当前 zookeeper 状态。

或者运行 netstat -lntup 命令查看网络状态,可以看到 zookeeper 的端口号 2181 已经被占用

注意没有关闭防火墙可能出现的问题!!!

如果你使用的阿里云服务器注意配置相关安全组:

1进入本实例安全组页面

2 选择配置规则

3 选择添加安全组规则,然后按照下图配置

在开始实战之前提个建议:尽量新建一个文件夹,然后后面将接口项目、服务提供者以及服务消费者都放在这个文件夹。

开始实战 2 :实现服务接口 dubbo-interface

主要分为下面几步:

1.创建 Maven 项目;

2.创建接口类

3.将项目打成 jar 包供其他项目使用

项目结构:

dubbo-interface 后面被打成 jar 包,它的作用只是提供接口。

  1. dubbo-interface 项目创建

File->New->Module… ,然后选择 Maven类型的项目,其他的按照提示一步一步走就好。

2.创建接口类

package top.snailclimb.service;

public interface HelloService {

public  String sayHello(String name);

}

3 将项目打成 jar 包供其他项目使用

点击右边的 Maven Projects 然后选择 install ,这样 jar 宝就打好了。

开始实战 3 :实现服务提供者 dubbo-provider

主要分为下面几步:

1.创建 springboot 项目;

2.加入 dubbo 、zookeeper以及接口的相关依赖 jar 包;

3.在 application.properties 配置文件中配置 dubbo 相关信息;

4.实现接口类;

5.服务提供者启动类编写

项目结构:

  1. dubbo-provider 项目创建

创建一个 SpringBoot 项目,注意勾选上 web 模块。不会创建的话,可以查看下面这篇文章:,可以说很详细了。

https://blog.csdn.net/qq_34337272/article/details/79563606

  1. pom 文件引入相关依赖

需要引入 dubbo 、zookeeper以及接口的相关依赖 jar 包。注意将本项目和 dubbo-interface 项目的 dependency 依赖的 groupId 和 artifactId 改成自己的。dubbo 整合spring boot 的 jar 包在这里找dubbo-spring-boot-starter。zookeeper 的 jar包在 Maven 仓库 搜索 zkclient 即可找到。

<dependency>
      <groupId>top.snailclimb</groupId>
      <artifactId>dubbo-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
  </dependency>
  <!--引入dubbo的依赖-->
  <dependency>
      <groupId>com.alibaba.spring.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>2.0.0</version>
  </dependency>
  <!-- 引入zookeeper的依赖 -->
  <dependency>
      <groupId>com.101tec</groupId>
      <artifactId>zkclient</artifactId>
      <version>0.10</version>
  </dependency>

3 在 application.properties 配置文件中配置 dubbo 相关信息

配置很简单,这主要得益于 springboot 整合 dubbo 专属的@EnableDubboConfiguration注解提供的 Dubbo 自动配置。

# 配置端口
server.port=8333

spring.dubbo.application.name=dubbo-provider
spring.dubbo.application.registry=zookeeper://ip地址:2181

4.实现接口

注意: @Service 注解使用的时 Dubbo 提供的而不是 Spring 提供的。另外,加了Dubbo 提供的 @Service 注解之后还需要加入

package top.snailclimb.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
import top.snailclimb.service.HelloService;

@Component
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
    return "Hello " + name;
    }
}

5 服务提供者启动类编写

注意:不要忘记加上 @EnableDubboConfiguration 注解开启Dubbo 的自动配置。

package top.snailclimb;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
// 开启dubbo的自动配置
@EnableDubboConfiguration
public class DubboProviderApplication {
public static void main(String[] args) {
    SpringApplication.run(DubboProviderApplication.class, args);
    }
}

开始实战 4 :实现服务消费者 dubbo-consumer

主要分为下面几步:

1 创建 springboot 项目;

2 加入 dubbo 、zookeeper以及接口的相关依赖 jar 包;

3 在 application.properties 配置文件中配置 dubbo 相关信息;

4 编写测试类;

5 服务消费者启动类编写

6 测试效果

项目结构:

第1,2,3 步和服务提供者的一样,这里直接从第 4 步开始。

4 编写一个简单 Controller 调用远程服务

package top.snailclimb.dubboconsumer;

import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.snailclimb.service.HelloService;

@RestController
public class HelloController {
@Reference
private HelloService helloService;

@RequestMapping("/hello")
public String hello() {
    String hello = helloService.sayHello("world");
    System.out.println(helloService.sayHello("SnailClimb"));
    return hello;
    }
}

5 服务消费者启动类编写

package top.snailclimb.dubboconsumer;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class DubboConsumerApplication {

public static void main(String[] args) {

    SpringApplication.run(DubboConsumerApplication.class, args);
    }
}
  1. 测试效果

浏览器访问 http://localhost:8330/hello 页面返回 Hello world,控制台输出 Hello SnailClimb,和预期一直,使用SpringBoot+Dubbo 搭建第一个简单的分布式服务实验成功!

sql表连接的几种方式

一.SQL 左外连接,右外连接,全连接,内连接

表结构

a表 id name b表 id job parent_id

1  张三      1 23   1 
2  李四      2 34   2 
3  王武      3 34   4

a.id同parent_id 存在关系

内连接

inner join与join的结果是一样的

select a.,b. from a inner join b on a.id=b.parent_id

结果是

1 张3 1 23 1

2 李四 2 34 2

左连接

select a.,b. from a left join b on a.id=b.parent_id

结果是

1 张3 1 23 1

2 李四 2 34 2

3 王武 null

右连接

select a.,b. from a right join b on a.id=b.parent_id

结果是

1 张3 1 23 1

2 李四 2 34 2

null 3 34 4

完全连接

select a.,b. from a full join b on a.id=b.parent_id

结果是

1 张3 1 23 1

2 李四 2 34 2

null 3 34 4

3 王武 null

注意:join 默认结果等于inner join

二、自然连接

natural join:自然连接(不允许带on/using)

natrual join:全自然连接,对左右2个表相同名字的列进行等值匹配,不可用on,using操作指定,自动删除多余重名列,自动合并不同的列

natrual left join:左自然连接,保留2个表的列(删除多余重名列),以左表为准,不存在匹配的右表列,值置为NULL

natrual right join:和左自然连接相反

select * from tb_test1 natural join tb_student; 
+—-+——–+——–+——-+————+ 
| id | name | gender | score | birthday | 
+—-+——–+——–+——-+————+ 
| 1 | 李毅 | 男 | 95.30 | 1988-03-20 | 
+—-+——–+——–+——-+————+

select * from tb_test1 natural left join tb_student; 
+—-+———–+——–+——-+————+ 
| id | name | gender | score | birthday | 
+—-+———–+——–+——-+————+ 
| 1 | 李毅 | 男 | 95.30 | 1988-03-20 | 
| 2 | 二丫 | 女 | 95.30 | NULL | 
| 3 | 张三 | 女 | 95.30 | NULL | 
| 4 | 李四 | 女 | 95.30 | NULL | 
| 7 | 胡鲁瓦 | 男 | 95.30 | NULL | 
| 9 | 后羿 | 男 | 95.30 | NULL | 
+—-+———–+——–+——-+————+

select * from tb_test1 natural right join tb_student; 
+—-+——–+——–+————+——-+ 
| id | name | gender | birthday | score | 
+—-+——–+——–+————+——-+ 
| 1 | 李毅 | 男 | 1988-03-20 | 95.30 | 
| 2 | kevin | 男 | 1987-08-23 | NULL | 
| 3 | marry | 女 | 1989-11-25 | NULL | 
| 4 | lucy | 女 | 1989-11-25 | NULL | 
| 5 | lily | 女 | 1992-01-25 | NULL | 
+—-+——–+——–+————+——-+

笛卡尔乘积
已上连接都是基于cross join(笛卡尔乘积),即两表行数的乘积

概念:没有where条件的交叉连接将产生连接表所涉及的笛卡尔积。即TableA的行数TableB的行数的结果集。(TableA 3行TableB 3行=9行)

sql语句:

select * from TableA cross join TableB

最全面的SpringBoot配置文件详解

Spring Boot在工作中是用到的越来越广泛了,简单方便,有了它,效率提高不知道多少倍。Spring Boot配置文件对Spring Boot来说就是入门和基础,经常会用到,所以写下做个总结以便日后查看。

1、配置文件

当我们构建完Spring Boot项目后,会在resources目录下给我们一个默认的全局配置文件 application.properties,这是一个空文件,因为Spring Boot在底层已经把配置都给我们自动配置好了,当在配置文件进行配置时,会修改SpringBoot自动配置的默认值。

配置文件名是固定的

application.properties

但我们可以修改为

application.yml

这两个文件本质是一样的,区别只是其中的语法略微不同。

2、值的写法

application.properties 配置文件比较简单,形式如下

key = value

application.yml 配置文件使用YMAL语言,YMAL不是如XML般的标记语言,更适合作为配置文件。

下面说下对于不同类型(字符串、数组)如何去规范书写。
2.1 数字,字符串,布尔

1、直接写

name=zhangsan

2、双引号

不会转义字符串里面的特殊字符,特殊字符会作为本身想表示的意思

name: “zhangsan \n lisi”

输出:
zhangsan
lisi

3、单引号

会转义特殊字符,特殊字符最终只是一个普通的字符串数据

name: ‘zhangsan \n lisi’

输出:

zhangsan \n lisi
2.2 对象、Map(属性和值)(键值对)

例如配置类中的字段为

Map<String,String> maps;

在yml配置文件中,行内写法

person.maps: {key1: value1,key2: value2}

需要注意:号后的空格,或者

person:
maps:
key: value

在properties配置文件中

person.maps.key=value

2.3 数组(List、Set)

在yml配置文件中

person:
list:

  • 1
  • 2
  • 3

行内写法

person:
list: [1,2,3]

在properties配置文件中

person.list[0]=1
person.list[1]=2
person.list[2]=3

3、自定义配置属性

Spring Boot提供自定义配置组件,拿前面举例的属性来写一个规范的配置文件

@Component // 或者@Configuration

@ConfigurationProperties(prefix = “person”)

public class Person {

private Map<String,String> maps;
private List<String> list;
private String name;

public Map<String, String> getMaps() {
    return maps;
}

public void setMaps(Map<String, String> maps) {
    this.maps = maps;
}

public List<String> getList() {
    return list;
}

public void setList(List<String> list) {
    this.list = list;
}
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}

@ConfigurationProperties 注解向Spring Boot声明该类中的所有属性和配置文件中相关的配置进行绑定。

prefix = "person":声明配置前戳,将该前戳下的所有属性进行映射。

@Component 或者@Configuration:将该组件加入Spring Boot容器,只有这个组件是容器中的组件,配置才生效。
4、配置自动提示

在配置自定义属性时,如果想要获得和配置Spring Boot属性自动提示一样的功能,则需要加入下面的依赖:


org.springframework.boot
spring-boot-configuration-processor
true

若是依旧无法自动提示,可以尝试开启IDE的Annonation Processing

5、配置属性校验

自定义配置文件时,可以使用@Validated注解对注入的值进行一些简单的校验,示例代码

@Validated

@Configuration

@ConfigurationProperties(prefix = “person”)

public class Person {

@Email
private String mail;

public String getMail() {
    return mail;
}

public void setMail(String mail) {
    this.mail = mail;
}

}

@Email 注解会对mail字段的注入值进行检验,如果注入的不是一个合法的邮件地址则会抛出异常。

其它常见注解:

@AssertFalse  校验false

@AssertTrue  校验true

@DecimalMax(value=,inclusive=)  小于等于value,inclusive=true,是小于等于

@DecimalMin(value=,inclusive=)  与上类似

@Max(value=)  小于等于value

@Min(value=)  大于等于value

@NotNull   检查Null

@Past   检查日期

@Pattern(regex=,flag=)  正则

@Size(min=, max=)  字符串,集合,map限制大小

@Validate   对po实体类进行校验

上述的这些注解位于javax.validation.constraints包下,具体用法查看注释即可了解。

6、自定义配置文件

除了在默认的application文件进行属性配置,我们也可以自定义配置文件,例如新建 peoson.properties ,配置内容如下

person.mail=yster@foxmail.com

然后在配置类中使用@PropertySource注解注入该配置文件

@Configuration

@ConfigurationProperties(prefix = “person”)

@PropertySource(value = “classpath:person.properties”)

public class Person {
private String mail;

public String getMail() {
    return mail;
}

public void setMail(String mail) {
    this.mail = mail;
}

}

测试@PropertySource注解不支持注入yml文件。

扩展:@ImportResource:该注解导入Spring的xml配置文件,让配置文件里面的内容生效。

例如:@ImportResource(locations = {"classpath:beans.xml"})

7、配置文件占位符

Spring Boot配置文件支持占位符,一些用法如下
7.1 随机数

${random.value}

${random.int}

${random.long}

${random.int(10)}

${random.int[1024,65536]}

7.2 默认值

占位符获取之前配置的值,如果没有可以是用:指定默认值

person.last-name=张三${random.uuid}

person.age=${random.int}

person.birth=2017/12/15

person.boss=false

person.maps.k1=v1

person.maps.k2=14

person.lists=a,b,c

person.dog.name=${person.hello:hello}_dog

person.dog.age=15

8、多配置文件
8.1 多Profile文件

我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml

默认使用application.properties的配置

8.2 yml支持多文档块方式

通过—可以把一个yml文档分割为多个,并可以通过 spring.profiles.active 属性指定使用哪个配置文件

server:
  port: 8081
spring:
      profiles:
        active: prod #指定使用哪个环境

---
server:
      port: 8083
spring:
      profiles: dev  #指定属于哪个环境


---

server:
      port: 8084
spring:
      profiles: prod  #指定属于哪个环境

8.3 激活指定profile

无论是使用上述多文档块的方式,还是新建application-dev.yml文件,都可以在配置文件中指定 spring.profiles.active=dev 激活指定的profile,或者

1、使用命令行:

java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar –spring.profiles.active=dev

可以直接在测试的时候,配置传入命令行参数

2、虚拟机参数:

-Dspring.profiles.active=dev

9、配置文件加载位置

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

–file:./config/

–file:./

–classpath:/config/

–classpath:/

优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件。

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;

我们还可以通过spring.config.location来改变默认的配置文件位置,示例:

java -jar spring-boot-demo-0.0.1-SNAPSHOT.jar –spring.config.location=G:/application.properties

10、外部配置加载顺序

SpringBoot也可以从以下位置加载配置,优先级从高到低,高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置。

1、命令行参数

所有的配置都可以在命令行上进行指定

java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar –server.port=8087 –server.context-path=/abc

多个配置用空格分开,形如 –配置项=值

2、来自java:comp/env的JNDI属性

3、Java系统属性(System.getProperties())

4、操作系统环境变量

5、RandomValuePropertySource配置的random.*属性值

由jar包外向jar包内进行寻找

优先加载带{profile}

6、jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件

7、jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件

再来加载不带profile

8、jar包外部的application.properties或application.yml(不带spring.profile)配置文件

9、jar包内部的application.properties或application.yml(不带spring.profile)配置文件

10、@Configuration注解类上的@PropertySource

11、通过SpringApplication.setDefaultProperties指定的默认属性

11、自动配置原理

11.1 自动配置原理

1、SpringBoot启动的时候加载主配置类,@EnableAutoConfiguration注解开启了自动配置功能。

2、@EnableAutoConfiguration 作用:

利用EnableAutoConfigurationImportSelector给容器中导入一些组件

可以查看selectImports()方法的内容;

List<String> configurations = getCandidateConfigurations(annotationMetadata,  attributes);获取候选的配置

SpringFactoriesLoader.loadFactoryNames()
扫描所有jar包类路径下  META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中

将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中

每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中,用他们来做自动配置。

Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
……

3、对每一个自动配置类进行自动配置功能。

4、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;

@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中

@ConditionalOnWebApplication
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;判断当前应用是否是web应用,如果是,当前配置类生效

@ConditionalOnClass(CharacterEncodingFilter.class)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;

@ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true)
//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;

public class HttpEncodingAutoConfiguration {

//他已经和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties;

//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}

@Bean   //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件
public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
    return filter;
}

根据当前不同的条件判断,决定这个配置类是否生效。

一但这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。

5、所有在配置文件中能配置的属性都是在xxxxProperties类中封装者,配置文件能配置什么就可以参照某个功能对应的这个属性类

@ConfigurationProperties(prefix = “spring.http.encoding”) //从配置文件中获取指定的值和bean的属性进行绑定

public class HttpEncodingProperties {

public static final Charset DEFAULT_CHARSET = Charset.forName(“UTF-8”);

精髓:

1) SpringBoot启动会加载大量的自动配置类

2) 先看我们需要的功能有没有SpringBoot默认写好的自动配置类

3) 再来看这个自动配置类中到底配置了哪些组件(只要我们要用的组件有,我们就不需要再来配置了)

4) 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值

xxxxAutoConfigurartion:自动配置类;给容器中添加组件;

xxxxProperties:封装配置文件中相关属性;

11.2 @Conditional注解

@Conditional派生注解(Spring注解版原生的@Conditional作用)

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效。

@Conditional扩展注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnExpression 满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项

自动配置类必须在一定的条件下才能生效。

我们怎么知道哪些自动配置类生效?

我们可以通过在properties(yml)启用 debug=true 属性来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效。

`============================

CONDITIONS EVALUATION REPORT

Positive matches:(自动配置类启用的)

CodecsAutoConfiguration matched:

- @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)

CodecsAutoConfiguration.JacksonCodecConfiguration matched:

- @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)

…….

Negative matches:(没有启动,没有匹配成功的自动配置类)

ActiveMQAutoConfiguration:

Did not match:

   - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)

AopAutoConfiguration:

Did not match:

   - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice', 'org.aspectj.weaver.AnnotatedElement' (OnClassCondition)`

参考

docs.spring.io官方文档:

https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#boot-features-external-config

java基础

一.java中的数据类型

1.基本数据类型:四类 八种

byte(1) boolean(1) short(2) char(2) int(4) float(4) long(8) double(8)

2.引用数据类型

String , 数组,集合ArrayList,Scanner,Random,自定义类型

二.引用数据类型String中的方法(4532)

第一组:判断方法

boolean equals(String str);//比较两个字符串的内容是否相等
boolean equalsIgnoreCase(String str);//比较两个字符串的内容是相等(忽略大小写)
boolean startsWith(String subStr);//判断某个字符串是否以指定的子串开头
boolean endsWith(String subStr);//判断某个字符串是否以指定的子串结尾

第二组:获取方法

int length();//获取字符串中字符个数
char charAt(int index);//获取字符串中某一个字符
String substring(int startIndex);//从指定下标开始截取字符串,直到字符串的末尾
String substring(int startIndex,int endIndex);//从指定下标开始截取字符串,到指定下标结束(包括开头不包括结尾)
---可忽略  int indexof(String subStr);//获取子串第一次出现的下标

第三组:转换方法

String toLowerCase();//转成小写串
String toUpperCase();//转成大写串
Char[] toCharArray();//变成字符数组

第四组:其他方法

String trim();//去掉字符串两端的空格
String[] split(String str);//切割字符串

三:流_读写文件

    输出流:数据从java程序 到  文件中
    FileWriter:文件的字符输出流,写数据(一个字符,一个字符串,一个字符数组)
        write(int ch);//写一个字符(可以写字符的ASCII码值)
        write(char[] chs);//写一个字符数组
        write(String s);//写一个字符串
        write(char[] chs,int startIndex,int len);//写一个字符数组的一部分
        write(String s,int startInex,int len);//写一个字符串的一部分

    输入流:数据从 文件 到java程序
    FileReader:文件的字符输入流,读数据(一个字符,一个字符数组)
        int read();//读取一个字符
        int read(char[] chs);//一个读取一个字符数组,返回值表示实际读取到的字符的个数

    文件的路径分为两种:
    1.相对路径:
        相对于当前项目而言的

    2.绝对路径:
        以盘符开头  C: D:
四:对象的内存图:
    Dog d = new Dog();
    d是引用数据类型,保存到栈(stack)中
    new Dog();创建对象,保存到堆(heap)中

五:this的作用以及本质

作用:区分局部变量和成员变量的同名的情况
本质:this代表一个对象,具体是哪一个对象,那么由方法的调用者决定

六:匿名对象:

语法: 只创建对象,而不是变量来接收
    比如: new Dog(); new Student()
特点:一个匿名对象 只能使用一次,第二次使用就是一个新的匿名对象

***继承

1.概念:
    描述两个类之间的关系(子类和父类之间的关系)
    一个类(子类) 在另外一个类(父类)的基础上创建,那么这个过程就叫做继承
2.语法:
    public class 父类{}
    public class 子类 extends 父类{}
3.继承的作用:
    子类自动拥有父类的可继承(非private修饰的)的成员变量和成员方法
    提高了代码的服用性
4.继承中子父类的成员变量和成员方法的特点:
    如果子父类中出现了同名的成员变量或者成员方法
        通过子类对象调用成员变量或者成员方法时,优先调用子类自己的,如果子类没有,再去访问父类的
5.java中继承的特点:
    1.java只支持单继承: 一个子类 只能有一个直接父类
    2.java中支持多层继承
    3.子类自动拥有父类的可继承(非private修饰的)的成员变量和成员方法