学习篇(三)

JAVA语言

一、编程基础

1、变量与标识符

数学名词:变数或变量,是指没有固定的值,可以改变的数。变量以非数字的付号来表达,一般用拉丁字母。变量是常数的想烦。变量的用于在于能一般化描述指令的方式。
计算机解释:变量就是系统为程序分配的一块内存单元,用来存储各种类型的数据。根据所存储的数据类型的不同,有各种不同类型的变量。变量名代表这块内存中的数据。
分类:按所属的数据类型划分:基本数据类型变量    引用数据类型变量
          按被声明的位置划分:局部变量(方法或语句块内部定义的变量)    成员变量(方法外部、类的内部定义的变量)
java对包、类、方法、参数和变量等要素命名时使用的字符序列成为标识符。
JAVA标识符命名规则:由字母、数字、下划线(_)和美元符号($)组成;不能以数字开头;区分大小写;长度无限制;不能是java中的关键字和保留关键字。
命名习惯:驼峰命名发、见名知意。以单词或单词组合来命名。

2、八种基本数据类型的声明

字节(byte)时计算机文件大小的基本计算单位。1字节=8位(bit)。bit是位或者比特,是计算机运算的基础;二进制中的一位,是二进制最小信息单位。二进位可以用来表示一个简单的额正/负判断,有两种状态的开关。在内存计算过程中,字节少的数据运算速度更快;在硬盘存储中,字节少的数据类型也可充分存入更多的数据。

数据类型 关键字 占用字节 取值范围 默认值
布尔型 boolean 不定值 true,false false
字节型 byte 1 -128~127 0
短整型 short 2 -32768~32767 0
整型 int 4 -2147483648~2147483647 0
长整型 long 8 -923372036854775808~9223372036854775807 0
字符型 char 2 0~65535 ‘\u0000'
单精度浮点型 float 4 1.4E-45~3.4028235E38 0.0F
双精度浮点型 double 8 4.9E-324~1.7976931348623157E308 0.0D

 

3、基本数据类型的声明

byte b = 10;
short s = 10;
int i = 10;
long lon = 100L;
float f = 10.98F;
double d = 10.123456D;
char c = 'A'
boolean bool = true;

4、进制与转换

计算机中的数据都以二进制数字保存。逢二进一。即只有0、1两个值。
计算机中信息的存储单位
位:表示一个二进制数码0或1,是计算机存储处理信息的最基本的单位。字节:一个字节由8个位组成。它表示作为一个完整处理单位的8个二进制数码。 

十六进制:因二进制表示法太冗长,所以在程序中一般喜欢用十六进制。用abcdef表示0-9之上的值。java中十六进制数据要以0x或0X开头。十六进制转换成二进制只需要将每个十六进制数字天幻为相对应的四个二进制位即可。

补码:一个正数的补码和其原码的形式是相同的。负数的补码是将该数的绝对值的二进制形式,按位取反再加1。二进制补码数据的最高位是符号位,0为正数,1为负数。好处:使用补码,可以将符号为和其他位统一处理,符号位有进位则进位被舍弃。 

二进制转换十进制
按权相加:把二进制数手写写成加权系数展开式,然后按十进制加法规则求和。
十进制转换二进制
整数部分:除2取余,逆序排列;小数部分:乘二取整,顺序排列。

5、基本数据类型转换

自动类型转换:容量小的类型自动转换成容量大的数据类型。
byte,short,int在计算中会转换成int类型
int转float,long转double不需要强制转换,但可能会丢失精度。
强制类型转换:容量大的类型转换成容量小的数据类型,需要加强制转换符(括号内加上要强转的数据类型)。
注意:强制类型转换可能造成精度降低,或者数据溢出。使用时要小心。
          特别的,boolean类型不能转换成任何其他数据类型。

6、关键字

abstract、assert、boolean、break、byte、case、catch、char、class、const、continue、default、do、double、else、enum、extends、final、finally、float、for、goto、if、implements、import、instanceof、int、interface、long、native、new、package、private、protected、public、return、strictfp、short、static、super、switch、synchronized、this、throw、throws、transient、try、void、volatile、while
其中,goto和const是保留关键字

7、转义字符

\n    换行,将当前位置移到下一行开头
\r    回车,将当前位置移到本行开头
\t    水平制表,跳到下一个TAB位置
\\    代表一个反斜线字符'\'
\'    代表一个单引号字符
\"    代表一个双引号字符

8、运算符与优先级

算术运算符:+  -  *  /  %  ++  --
表达式:由变量、常量、运算符组成的式子。
赋值运算符:=  +=  -+  *=  /=  %=
关系运算符:==  !=  <  >  <=  >=  
逻辑运算符:与&、或|、异或^、非!、短路与&&、短路或||
位运算符:对两个操作数中的每一个二进制位都进行运算。按位取反~;按位与&;按位或|;按位异或^。
位移运算符:左移:将二进制形式的a逐位左移b位,最低位空出的b位补0;带符号右移:将二进制形式的a逐位右移b位,最高为空出的b位补原来的符号位;无符号右移:将二进制形式的a逐位右移b位,最高为空出的b位补0
三目运算符:X ? Y : Z  X为boolean类型表达式,先计算X的值,若为true,整个三目运算的结果为表达式Y的值,否则为Z的值。

优先级:
 

优先级 运算符 综合性
1 ()  []

从左向右

2 !  +  -  ~  ++  -- 从右向左
3 *  /  % 从左向右
4 +  -  (加减) 从左向右
5 <<  >>  >>> 从左向右
6 <  <=  >  >=  instanceof 从左向右
7 ==  != 从左向右
8 & 从左向右
9 ^ 从左向右
10 | 从左向右
11 && 从左向右
12 || 从左向右
13 ? : 从右向左
14 =  +=  -=  *=  /=  %=  &=  |==  ^=  ~=  <<=  >>=  >>>= 从右向左

9、分支语句

if条件结构是根据条件判断之后再作处理。
if (表达式){
    执行语句;
}else{
    执行语句;
}
如果...那么...。执行过程:执行到if时,先计算()里表达式的值,表达式的值为真,执行()后的{}里的执行语句,如果为假,执行else后边的{}执行语句。else可以没有,且在else后可继续跟if...else...结构。

switch
swich (表达式){
    case 取值1: 语句块1;
        break;
    case 取值2: 语句块2;
        break;
    case 取值n: 语句块n;
        break;
    default: 语句块n+1;
        break;
}
表达式返回值必须是int/byte/char/short/string。
case子句中的取值必须是常量,且所有case子句中的取值应该不同
default可选
break语句用来在执行完一个case分支后使程序跳出switch。如果没写break则直接往下执行
case后面的执行体可写{}也可不写。 

比较:
if  switch
相同:都是选择分支语句
不同:语法不同;语句后表达式值类型不同;使用场景不同,if通常用于变量值在某个区间内,switch变量的值时某个固定值。
if  三目
三目运算符都可以使用if实现,但反之不行 

经验:
1、在实现分支判断时,把匹配成功率高的判断放在前面,可以提高效率
2、在表达式中判断,尽量使用确定值和不确定值判断(确定值在左)。

10、循环语句

while  do...while  for

while(条件表达式){
    语句块;
}
符合条件,继续执行,否则退出。
特点:先判断,再循环。 

do{
    循环操作;
}while(循环条件);
先执行一遍循环操作,符合条件,循环继续执行;否则退出。
特点:先执行,再判断。

for (初始化参数;判断条件;更新循环变量){
    循环体;
}
关键字:continue表示跳过当前循环,继续下次循环。
写法:
1、标准写法;
2、表达式1省略,但在外部声明;
3、表达式2省略,死循环;
4、表达式3省略,但需要在循环内部声明;
5、表达式1,3省略,要分别在循环内外声明;
6、三个表达式都省略,死循环。

二、方法与数组

方法(函数)是一段特定功能的代码块。方法可提高程序的复用性和可读性。 
格式:
    访问权限修饰符 [其他修饰符 static等] 返回值类型 方法名(参数类型1 形参1, 参数类型2 形参2, ...){
        // 方法体
        return 返回值;
    } 
实际参数:实际参与运算的参数
形式参数:方法定义上的,用于接收实际参数。
注意:若当前方法中不需要形参,那么形参列表可以为空;形参和实参的类型要相互兼容,且实参的取值范围要小于等于形参类型的取值范围。
形参就是一个变量,实参就是一个值,传参就是把实参值传给形参
return 用于结束方法。
注意:若方法没有返回值,即返回值类型是void那么可以不写return;
          return即表示结束一个方法,也可以将返回值返回给调用当前方法的调用者;
          return返回值时一次只能返回一个值,不可以反回多个值;
          一个方法中可以有多个return,但被执行的只能有一个,所以需要判断
方法重载(overloading method)
在类中可以创建多个方法,使他们具有相同的名字,但参数、定义不同。

数组
背景:当有一组相同类型的数据需要存储,如果使用单个变量来存储,将定义若干个变量名,这样回非常繁琐,并且难以维护。
定义:
    使用默认的初始值来初始化数组中的每一个元素。数组元素类型[] 数组名 = new 数组元素类型[数组中元素的个数]
    先声明,然后再赋予默认的初始值。数组元素类型[] 数组名;    数组名 = new 数组元素类型[数组中元素的个数]
    先声明,然后使用指定的值进行初始化。数组元素类型[] 数组名 = new 数组元素类型[] {元素1, 元素2, ....}
    将第三种方法简化为:数组元素类型[] 数组名 = {元素1, 元素2, .... }
数组的遍历:x.length数组长度。下标从0开始,到数组长度-1。
遍历方式:for循环数组下标;for(数组中元素的类型 变量:数组名){    数组中元素的类型 临时变量 = 变量;}
注意:空指针异常、数组越界异常。

排序算法
冒泡排序:比较相邻的元素,如果第一个比第二个大,就交换;对每一对相邻元素作同样的工作,从开始第一对到结尾最后一对,此时最后的元素应该是最大的数;针对所有的元素(除最后一个外)重复以上步骤,直到没有任何一对数字需要比较。    稳定排序算法。
选择排序:每一趟从待排序的数据元素中选出最小(最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。    不稳定的排序算法。
直接插入排序:每步将一个待排序的记录,按其顺序码大小插入到前面已排序的子序列的合适位置,直到全部插入排序完成。
二分查找算法:又叫折半查找。前提是再已经排序好的数组中,通过将待查找的元素与中间索引值对应的元素进行比较,大于去右半部分,小于去左半部分。直到找到/找不到返回一个负数。

 

Array工具类
用于操作数组(排序、搜索等)的各种方法
二分法查找:Arrays.binarySearch(int[] array, int value);
数组内容转成字符串输出:Arrays.toString(int[] array);
数组排序:Arrays.sort(int[] array)
复制指定的数组:Arrays.copyOf(int[] array, int length);    Arrays.copyOf(int[] array, int from int to);    System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
判断两个数组是否相等:Arrays.equals();
使用指定元素填充数组:Arrays.fill()

三、面向对象

面向对象是一种编程思想,是一种思考问题的思维方式。
先整体,再局部;先抽象,再具体;能做什么,再怎么做。

类与对象

类:分类、类别。通过分类我们可以区别不同的事物种类。类是一组具有相同特性(属性)与行为(方法)的事物集合。
类与对象:类表示一个共性的产物,是一个综合的特征;对象是一个个性的产物,是一个个体的特征。类由属性和方法组成:属性相当于特征;方法相当于行为。
格式:
类定义
class 类名称{
    属性名称;
    返回值类型 方法名称(){}
}
对象定义
类名称 对象名称 = new 类名称()    // 对象的声明可以为null,声明后无法使用。
属性、方法的访问
对象.属性;
对象.方法();  // 可以通过new 类名称().方法名()来匿名调用类方法。
对象的内存:new 关键字表示创建对象;实例化对象;申请内存空间。若对象没有申请内存空间,会报空指针异常(java.lang.NullPointerException)。

小结:
    1、new关键字:表示向内存申请空间,也表示实例化一个对象,创建一个对象。
    2、一个对象在内存中的大小,由该对象的所有属性所占的内存大小的总和决定。引用类型变量在32位系统上占4个字节,在64位系统上占8个字节。加上额外的对象隐性数据所占大小。
    3、相同的类型才可以赋值。
    4、不同的引用,指向同一个对象,任何一个引用改变对象的值,其他引用都会反映出来。
    5、编程时要注意的问题,在确定不适用对象时,要尽早释放对象:引用 = null
    6、当一个堆中的对象没有被任何引用变量所指向时,该对象会被JVM的GC程序认为是垃圾对象,从而被回收。

封装性

定义:隐藏实现的细节,仅对外提供访问接口。包括属性的封装、方法的封装、类的封装、组件的封装、模块化封装、系统级封装......
好处:模块化;信息隐藏;代码重用;插件化易于调试;具有安全性
缺点:会影响执行效率
示例:

// 没有封装
class Person{
    String name;
    int age;
}


// 封装后
class Person{
    private String name;
    private int age;

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


public class Test{
    public static void main(String[] args){
        Person p1 = new Person();
        p1.name = "菲菲";
        p1.age = 18;
    }
}

如果属性/方法没有封装,那么在本类之外创建对象之后,可以直接访问属性;
使用private关键字声明的属性/方法,只能在本类中访问,如果想在类外部访问私有属性,需要提供公有的方法来间接访问。
通常在一个类中,属性都是私有的,并对外提供getter、setter方法。

成员变量和局部变量

在类中位置不同:成员变量在类中定义;局部变量在方法中定义或者是方法的参数
在内存中的位置不同:成员变量在堆内存(属于对象,对象进堆内存);局部变量在栈内存(属于方法,方法进栈内存)
生命周期不同:成员变量随着对象的创建/销毁而存在/消失;局部变量随方法的调用/完成而存在/消失
初始化值不同:成员变量有默认值,引用类型默认位null;局部变量没有默认初始化值,必须定义,赋值,然后使用。
ps:局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用就近原则

构造方法

类构造对象时调用的方法,用于对象的初始化工作;是实例化一个类的对象时(new),最先调用的方法。
需要在类中定义,构造方法的定义格式:方法名称与类名称相同,无返回值类型声明。
构造方法重载:
无参:public Dog(){}  // 如果没写/写了但没传参数,会调用无参构造方法。
带一个参数:public Dog(String name){    this.name = name; }
带多个参数:public Dog(String name, int age){    this.name = name;    this.age = age;}
在构造方法内部调用其他构造方法:this(name); 需注意,必须为第一行。且相互调用时必须有出口。
总结:
    1、构造方法名称与类名相同,没有返回值声明;
    2、构造方法用于初始化数据(属性);
    3、每一个类中都会有一个默认的无参构造方法;
    4、如果类中有显示的构造方法,那么默认的构造方法无效;
    5、如果有显示的构造方法,还想保留默认构造方法,需要自己写出来;
    6、构造方法可以有多个,但参数不一样,成为构造方法的重载;
    7、在构造方法中调用另一个构造方法,使用this(...),该句代码必须在第一句;
    8、构造方法之间的调用,必须有出口;
    9、给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留;
    10、一个好的编程习惯是保留默认的构造方法(为了方便一些框架代码使用反射来创建对象);
    11、private Dog(){}; 构造方法私有化,当我们需要保证该类只有一个对象时(单例,工具类等),可以使用。

This关键字

在java基础中,this关键字是一个最重要的概念,使用this关键字可以调用类中的属性;调用类的方法或构造方法;表示当前对象。

值传递与引用传递

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递是指在调用函数是将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

值传递会创建副本,所以在函数中无法改变原始对象;引用传递不创建副本,所以在函数中可以改变原始对象。

JAVA中只有值传递(值内容为对象的引用),但是对于对象参数,值的内容时对象的引用。引用例子https://www.zhihu.com/question/31203609:
你有一把钥匙,当你的朋友想去你家时,如果你直接把你的钥匙给他,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事,比如刻了自己的名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
同样的情况下,你复刻一把钥匙给他,这是值传递,你自己的钥匙还在你手里。这样,无论他对钥匙做什么,都不会影响到你的钥匙。
无论是以上哪种情况,你朋友拿着你给他的钥匙,进到你家,把你家电视砸了,那你的电视就真的被砸了。即改变实际参数的属性时。

扩展:JAVA中的参数传递严格意义上说应该是共享传递。共享传递是指在调用函数时,传递给函数的是实参的地址的拷贝(如果实参在栈中,则直接拷贝该值)。在函数内部对参数进行操作时,需要先根据拷贝的地址寻找到具体的值,在进行操作。如果该值在栈中,那么因为是直接拷贝的值,所以函数内部对参数进行操作不会对外部变量产生影响。如果原来拷贝的是原值在堆中的地址,那么需要现根据该地址找到堆中对应的位置,再进行操作。因为传递的是地址的拷贝所以函数内对值的操作对外部变量是 可见的。

对象的关系

一对一

一对多

多对多

static关键字

修饰一个属性:声明为static的变量实质上就是全局变量。
修饰一个方法:通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法。
    静态变量或方法不属于对象,依赖类;只有存一份,所有对象共享,存储在静态方法区;建议不要使用对象名去调用静态数据,直接使用类名去调用。
    static修饰一个方法,那么该方法属于类,不属于对象,直接用类名调用;静态方法不能访问非静态属性/方法,只能访问静态属性/方法(因为生命周期不同);不能以任何方式引用this/super(因为不需要依赖对象实例)。
    所有对象共同的属性或方法,即可定义为静态的。
修饰一个类:内部类。

main方法:其修饰符通常为public(公有的,最大的访问权限)  static(静态的,无需创建对象)  void(没有返回值,无需向JVM返回结果)  main(固定方法名)  形参:String[] args  表示参数为字符串数组,可以在调用方法时传入参数。

代码块

普通代码块:在方法中写的代码块
构造代码块:在类中定义的代码块,创建对象时被调用,优先于构造方法执行。
静态代码块:在类中使用static声明的代码块成为静态代码块,只在第一次使用的时候被调用(创建对象),只会执行一次,优于构造快执行,在项目开发中,通常会使用静态代码块来初始化只调用一次的数据,比如配置文件。
同步代码块:(多线程讲)
重点会使用的时静态代码块,普通代码块,同步代码块,构造代码块。

单例

单例设计模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现:构造方法私有化,声明一个本类对象,给外部提供一个静态方法获取对象实例。
实现方式:饿汉式(在类被加载后,对象被创建,到程序结束后释放。占用内存的时间长,提高效率);懒汉式(在第一次调用getInstance方法时,对象被创建,到程序结束后释放。占用内存时间短,效率低)。

// 饿汉式
class Singleton1{
    private Sigleton1(){}
    private static Singleton1 s = new Singleton1();
    public static Singleton1 getInstance(){
        return s;
    }
}

// 懒汉式
// 以下代码在多线程访问时会有安全问题,后续会补充。
class Singleton2{
    private Sigleton2(){}
    private static Singleton2 s;
    public static Singleton2 getInstance(){
        if(s==null){
            s = new Singleton2();
        }
        return s;
    }
}

应用场景(优点):设计工具类,工具类大多只有方法,不需要属性;工具类可能会被频繁调用。目的是为了节省重复创建对象所带来的内存消耗,从而提高效率。
使用构造方法私有化+静态方法可以替代单例设计模式,但通常不推荐使用,静态方法自从创建之后,方法、变量等会一直占用内存。

// 使用构造方法私有化+静态方法替代单例
class Tools{
    private Tools(){};
    public static void print1(){}
    public static void print2(){}
}

对象数组

对象数组就是数组里的每个元素都是类的对象,赋值时先定义对象,然后将对象直接赋值给数组。
动态数组:数组时一种线性数据结构;数组不适合作删除插入等操作,适合添加,查找,遍历。

继承

继承是从已有的类创建新类的过程。是面向对象的三大特征之一(封装,多态)。被继承的类成为父类(超类),继承父类的类称为子类(派生类)。继承指一个对象直接使用另一个对象的属性和方法。通过继承可以实现代码重用。

语法: [访问权限修饰符] class 子类名 extends 父类名{    类体定义; }

protected:受保护的访问权限修饰符,用于修饰属性和方法,使用protected修饰的属性和方法可以被子类继承。

限制:Java只能实现单继承,也就是一个类只能有一个父类;允许多层继承,即有爹有爷爷;继承只能继承非私有属性和方法;构造方法不能被继承(可以通过super调用);创建子类对象时,父类的构造方法也会被调用,因为子类要使用到父类的数据;创建子类对象时会调用父类的默认构造方法;

小结:
1、继承是发生在多各类之间的;
2、继承使用关键字extends;
3、JAVA只能单继承,允许多层继承;
4、被继承的类叫父类(超类),继承父类的类叫子类(派生类);
5、在父类中的非私有属性和方法可以被子类继承;
6、protected修饰的属性和方法可以被子类继承;
7、构造方法不能被继承;
8、创建对象会调用构造方法,调用构造方法不一定会创建对象;
9、实例化子类对象,会先调用父类构造方法,如果父类中没有默认的构造方法,那么子类必须显示的通过super(...)来调用弗雷德带参构造方法,super只能在子类构造方法中的第一句。
优点:提高代码的复用性、维护性,让类与类之间产生关系,是多态的前提。
缺点:增强了类与类之间的耦合性。
ps:开发原则:高内聚,低耦合。

子类实例化过程:子类进行实例化操作时,首先会先让父类进行初始化操作。之后子类再自己进行实例化操作。
构造方法只是用于初始化类中的字段以及执行一些初始化代码,并不代表会生成对象

重写(overriding method)

子类可以继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动的继承父类的方法,而是想做一些修改。方法重写又称方法覆盖。
在子类和父类中,重写方法后,在调用时,已创建的对象类型为准,就近调用。
特性:发生在自父类中,方法重写的两个方法返回值、方法名、参数列表必须完全一致;子类异常不能大于父类异常;子类访问级别不能低于父类访问级别;父类方法中若有private/static/final中的一个修饰符修饰,那么子类不能重写这个方法。
目的:为满足子类特有的需求,是程序扩展的体现。
重写和重载的区别:
重载:方法的重载,发生在同一个类中,方法名相同,参数列表不同,返回值无关。
重写:方法的重写,发生在子父类中,方法名相同,参数列表相同,返回值相同,子类的访问修饰符要大于等于父类的访问修饰符,异常声明小于等于父类异常声明,如果方法被private、static、final修饰,那么不能被重写。

super可以调用父类中的属性,可以从父类实例处获得信息;调用父类中的方法,可以委托父类对象帮助完成某件事情;调用父类中的构造方法,必须在子类构造方法的第一条语句,调用父类中相应的构造方法,若不显示的写出来,则默认调用父类的无参构造方法。

final关键字
声明常量:修饰属性或修饰局部变量(最终变量),也称为常量。常量命名规则建议使用全大写。
声明方法:最终方法,只能被子类继承,不能被子类重写。
声明类:最终类,没有子类,final修饰的类无法被继承。
方法参数中使用:在该方法内部不能修改参数值。 
应用:常量;常量类。

抽象类

概念:很多具有相同特征和行为的对象可以抽象为一个类;很多具有相同特征和行为的类可以抽象为一个抽象类。使用abstract关键字声明的类为抽象类。
规则:抽象类可以没有抽象方法,有抽象方法的类必须是抽象类;非抽象类继承抽象类必须实现所有抽象方法;抽象类可以继承抽象类,可以不是先父类抽象方法;抽象类可以有方法实现和属性;抽象类不能被实例化;抽象类不能声明为final;抽象类可以有构造方法。

接口

格式:interface 接口名称{
    全局常量;
    抽象方法;
}
接口中定义的方法没有生命修饰符,默认为public abstract。
概念:接口是一组行为的规范、定义、没有实现;使用接口,可以让我们的程序更加利于变化;接口是面向对象编程体系中的思想精髓之一;面向对象设计法则:基于接口编程。
接口之间可以多继承(类只能单继承),接口实现可以实现多个接口,但必须实现接口的所有方法。。
使用规则:定义一个接口,使用Interface关键字;在一个接口中,只能定义常量、抽象方法、默认的实现方法;接口可以继承多个接口:extends xxx, xx;一个具体类实现接口使用implements关键字;一个类可以实现多个接口;抽象类实现接口可以不实现接口的方法;在接口中定义的方法没有声明访问修饰符,默认为public;接口不能有构造方法;接口不能被实例化。
面向对象设计原则:对修改关闭,对扩展开放;面向接口编程。

多态性

对象在运行过程中的多种形态。分为两类:方法的重载与重写;多项的多态性。
在编程时针对抽象类型的编写代码,称为面向抽象编程(或面向接口编程)。父类通常都定义为抽象类、接口。

对象的多态性是从继承关系中的多个类而来。
向上转型:将子类实例转为父类引用,格式:父类 父类对象 = 子类实例;(自动转换)
向下转型:将父类实例转为子类实例,格式:子类 子类对象 = (子类) 父类实例;(强制转换)

小结:方法的重载与重写就是方法的多态性表现;多个子类就是父类中的多种形态;父类引用可以指向子类对象,自动转换;子类对象指向父类引用需要强制转换;在实际开发中尽量使用父类引用(利于扩展)。

instanceof关键字

用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,避免发生类型转换异常。
对象 instanceof 类型    返回Boolean类型值。
该语句一般用于判断一个对象是否为某个类的实例,是返回true,否返回false。

父类的设计原则:通过instanceof关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得很繁琐。所以:父类通常情况下都设计为抽象类或接口,并且优先考虑接口,接口不满足才考虑抽象类;一个具体的类尽可能不去继承另一个具体的类,这样的好处是无需检查对象是否为父类的对象。

抽象类应用:模板方法模式

定义一个操作中的算法的骨架,而将一些可变部分的实现延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤。
通俗讲:模板类定义好事件操作流程,子类继承后实际操作,并加入定制内容。

接口应用:策略设计模式

定义了一系列算法,经每一种算法封装起来并可以相互替换使用,策略模式让算法独立与使用它的客户应用而独立变化。
OO设计原则:面向接口编程(面向抽象编程);封装变化;多用组合,少用继承。

Object类

类层次结构的根类,每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法。所有类都是Object类的子类。
toString()方法,返回该对象的字符串表示。
equals(Object obj)方法。指示其他某个对象是否与此对象相等。equals方法在非空对象引用上实现相等关系:自反性、对称性、传递性、一致性。
finalize()方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写finalize方法,已配置系统资源或执行其他清楚。
getClass()返回此Object的运行时类。  

简单工厂设计模式

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。一般由工厂类、产品抽象类、产品类组成。由工厂类处理生产产品的逻辑。

静态代理设计模式

为其他对象提供一种代理以控制对这个对象的访问。说白了就是“真实对象”的代表,在访问对象时引入一定程度的间接性,因为这种间接性可以附加多种用途。

适配器设计模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
OO设计原则:面向接口编程(面向抽象编程);封装变化;多用组合,少用继承;对修改关闭,对扩展开放。

内部类

就是在一个类的内部定义的类。
格式:
class Outer{
    class Inner{}
}
编译后会产生Outer.class和Outer$Inner.class。
1、成员内部类:直接在类中定义的类。可在外部对内部类实例化:outer.new Inner();(不建议)。建议在外部类中定义一个方法,对外提供访问内部类的接口。
2、方法内部类:在一个类中的方法内定义一个类。方法内部类只能在定义该内部类的方法内实例化,不可以再此方法外对其实例化;方法内部类对象不能使用该内部类所在方法的非final局部变量。
3、静态内部类:在一个类内部定义一个静态内部类:静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能访问它。静态嵌套类仅能访问外部类的静态成员和方法。
4、匿名内部类:匿名内部类就是没有名字的内部类。分为继承式的匿名内部类、接口式的匿名内部类、参数式的匿名内部类。原则:不能有构造方法,只能有一个实例;不能定义任何静态成员、静态方法;不能是public、protected、private、static;一定是在new的后面,用其隐含实现一个接口或继承一个类;匿名内部类为局部的,所以局部内部类的所有限制都对其生效。

局部内部类访问局部变量必须用final修饰:当调用这个方法时,局部变量如果没有使用final修饰,他的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也马上消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已经无法使用过了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了。在jdk1.8中取消了必须显示的使用final修饰,编译器默认会为这个变量加上final。

内部类作用:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

依赖外部类对象的:成员内部类,方法内部类,匿名内部类。
静态内部类不依赖外部类的对象。
所以在项目中优先考虑选择静态内部类(不会产生内存泄漏)。

递归

是一种直接或者间接的调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题时十分有效的,它往往使算法的描述简洁而且易于理解。
递归必须要有出口;递归内存消耗大,容易发生溢出。

链表

一种常见的常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到是下一个节点的指针。

/**
    链表
    一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,
    而是在每一个节点里存下一个节点的指针。
*/

public class Test15{
	public static void main(String[] args){
		
		NodeManager nm = new NodeManager();
		System.out.println("------add----------");
		nm.add(5);
		nm.add(4);
		nm.add(3);
		nm.add(2);
		nm.add(1);
		nm.print();
		System.out.println("-------del---------");
		nm.del(3);
		nm.print();
		System.out.println("-------find---------");
		System.out.println(nm.find(1));
		System.out.println("-------update---------");
		nm.update(1,10);
		nm.print();
		System.out.println("-------insert---------");
		nm.insert(1,20);
		nm.print();
		
	}
}


class NodeManager{
    private Node root; // 根节点
    private int currentIndex = 0;
    public void add(int data){
        if(root==null){
            root = new Node(data);
        }else{
            root.addNode(data);
        }
    }

    public void del(int data){
		if(root==null)return;
		if(root.getData()==data){
			root = root.next;
		}else{
			root.delNode(data);
		}
	}

    public void print(){
		if(root!=null){
			System.out.print(root.getData()+"->");
			root.printNode();
			System.out.println();
		}
	}

    public boolean find(int data){
		if(root==null)return false;
		if(root.getData()==data){
			return true;
		}else{
			return root.findNode(data);
		}
	}

    public boolean update(int oldData,int newData){
		if(root==null)return false;
		if(root.getData()==oldData){
			root.setData(newData);
			return true;
		}else{
			return root.updateNode(oldData,newData);
		}
	}

    public void insert(int index,int data){
		if(index<0)return;
		currentIndex = 0;
		if(index==currentIndex){
			Node newNode = new Node(data);
			newNode.next = root;
			root = newNode;
		}else{
			root.insertNode(index,data);
		}
	}

    
    private class Node{
        private int data;
        private Node next; // 把当前类型作为属性

        public Node(int data){
            this.data = data;
        }

        public void setData(int data){
            this.data = data;
        }
        public int getData(){
            return data;
        }

        // 添加节点
        public void addNode(int data){
            if(this.next==null){
                this.next = new Node(data);
            }else{
                this.next.addNode(data);
            }
        }

        // 删除节点
        public void delNode(int data){
            if(this.next!=null){
                if(this.next.data==data){
                    this.next = this.next.next;
                }
            }else{
                this.next.delNode(data);
            }
        }

        // 输出所有节点
        public void printNode(int data){
            if(this.next!=null){
                System.out.print(this.next.data + "->");
                this.next.printNode();
            }
        }

        // 查找节点是否存在
        public boolean findNode(int data){
			if(this.next!=null){
				if(this.next.data==data){
					return true;
				}else{
					return this.next.findNode(data);
				}
			}
			return false;
		}public void findNode(int data){
            return false;
        }

        // 修改节点
        public boolean updateNode(int oldData,int newData){
			if(this.next!=null){
				if(this.next.data==oldData){
					this.next.data = newData;
					return true;
				}else{
					return this.next.updateNode(oldData,newData);
				}
			}
			return false;
		}

        // 插入节点
        public void insertNode(int index,int data){
			currentIndex++;
			if(index==currentIndex){
				Node newNode = new Node(data);
				newNode.next = this.next;
				this.next = newNode;
			}else{
				this.next.insertNode(index,data);
			}
		}
    }
}

链表与数组:都是线性数据结构。
数组适合查找,遍历,固定长度;
链表适合插入,删除,不宜过长,否则会导致遍历性能下降。

包装类

 Number:Integer、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字。
Object:Character、Boolean都是Object的直接子类

把基本类型转换为包装类,称为自动装箱:Integer i1 = new Integer(10);
把包装类转换为基本类型,称为自动拆箱:int i2 = i1.intValue();
建议使用:Integer i3 = 10;
Integer i4 = new Integer("123");
转型:Integer.parseInt("12");// 把数值字符串转换为int    Integer i6 = Integer.valueOf("12");
int i7 = Integer.valueOf("12");// 此处可以运行,但系统会额外做一次拆箱操作。 

享元模式:使用共享对象,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;适用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。(运用共享技术有效的支持大量细粒度的对象)

包用于对多个java源文件的管理,就像文件目录一样。
定义:package com.vince;
语句只能出现在代码中的第一句。

访问修饰符 同一个类 同包 不同包子类 不同包非子类
public
protected  
默认    
private      

OO原则:
开闭原则:一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭;
合成/聚合复用原则:新对象的某些功能在已创建好的对象里已经实现,那么尽量用已有对象提供的功能,使之称为新对象的一部分,而不要再重新创建;
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
迪米特法则:一个对象应该对其他对象保持最少的了解;
里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象;
单一职责原则:不要存在多余一个导致类变更的原因,即一个类只负责一项职责。

四、常用类库

字符串操作

String可以表示一个字符串,实际上是使用字符数组存储的。
String的两种赋值方式:
直接赋值:String s1 = "";
使用new关键字创建对象:String s2 = new String("");    //此处可能创建两个对象。过程:先在常量池里创建(如果有就不创建),然后再创建对象。此时s1 != s2。
直接赋值:如果再编译期可以确定值,那么就使用已有的对象,否则会创建新的对象。

字符串操作方法:
字符与字符串
public char charAt(int index)  普通方法  根据下标找到制定的字符
public char[] toCharArray()  普通方法  以字符数组的形式返回全部的字符串内容
public String(char[] value)  构造方法  将全部的字符数组变为字符串
public String(char[] value, int offset, int count)  构造方法  将指定范围的字符数组变为字符串。
字节与字符串
public byte[] getBytes()  普通方法  将字符串变为字节数组
public String(byte[] bytes)  构造方法  将字节数组变为字符串
public String(byte[] bytes, int offset, int length)  构造方法  将指定范围的字节数组变为字符串
public String (byte[] bytes, String charsetName)  构造方法  通过指定的charset解码指定的byte数组,构造一个新的String
判断是否以指定内容开头/结尾
public boolean startsWith(String prefix)  普通方法  从第一个位置开始判断是否以指定内容开头
public boolean startsWith(String prefix, int toffset)  普通方法  从指定位置开始判断是否以制定的内容开头
public boolean endsWith(String suffix)  普通方法  判断是否以指定的内容结尾
替换操作
public String replace(char oldChar, char newChar)  普通  替换指定字符
public String replace(CharSequence target, CharSequence 普通  替换指定字符串
public String replaceAll(String regex, String replacement)  普通  替换所有指定的字符串
public String replaceFirst(String regex, String replacement)  普通  替换第一个满足条件的字符串
截取操作
public String substring(int beginIndex)  普通  从指定位置开始一直截取到末尾
public String substring(int beginIndex, int endIndex)  普通  截取指定范围的字符串
拆分操作
public String[] split(String regex)  普通  按照指定的字符串拆分
public String[] split(String regex, int limit)  普通  拆分字符串,并指定拆分的个数
查找操作
public boolean contains(String s)  普通  返回一个字符串是否存在
public int indexOf(int ch)  普通  从头查找制定的字符是否存在,如果存在返回位置,不存在返回'-1'
public int indexOf(int ch, int fromIndex)  普通  从指定位置查找指定的字符是否存在,如果存在返回位置,不存在返回'-1'
public int indexOf(String str)  普通  从头查找指定字符串是否存在
public int indexOf(String str, int fromIndex)  普通  从指定位置查找字符串是否存在
public int lastIndexOf(int ch)  普通  从字符串的最后向前查找
public int lastIndexOf(int ch, int fromIndex)  普通  从字符串的指定位置向前查找
public int lastIndexOf(String str)  普通  从字符串的最后向前查找
public int lastIndexOf(String str, int from Index)  普通  从字符串的指定位置向前查找
其他
public boolean isEmpty()  普通  判断是否为空,指内容为空
public int length()  普通  取得字符串的长度
public String toLowerCase()  普通  转小写
public String toUpperCase()  普通  转大写
public String trim()  普通  去掉开头结尾的空格
public String concat(String str)  普通  字符串连接操作

StringBuffer
使用String连接字符串,代码性能会非常低,因为String的内容不可改变。
StringBuffer目的是来解决字符串相加时带来的性能问题(常量相加不会有性能问题);内部实现采用字符数组,默认长度为16,若超出会*2+2动态扩充,所以预知要添加的数据长度时,建议直接指定长度;是线程安全的,会影响性能。
常用方法
public StringBuffer    构造一个空的StringBuffer对象
public StringBuffer(String str)    将指定的String变为StringBuffer的内容
public StringBuffer(CharSequence seq)    接受CharSequence接口的实例
public StringBuffer append(数据类型 b)    提供了多种类型的append方法,用于字符串连接
public StringBuffer delete(int start, int end)    删除指定位置的内容
public int indexOf(String str)    查询字符串
public StringBuffer insert(int offset, 数据类型 b)    在指定位置上增加一个内容
public StringBuffer replace(int start, int end, String str)    将指定范围的内容替换成其他内容
public String substring(int start, int end)    截取指定范围的字符串
public String substring(int start)    字符串截取
public StringBuffer reverse()    字符串反转

StringBuilder
一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步。该类被设计用作StringBuffer的一个建议替换,用在字符出啊你缓冲区被单个线程使用的时候。如果可能,建议优先采用StringBuilder。

程序国际化

Internationalization(I18N)同一套代码可以在各个语言环境下进行使用。各个语言环境下,指示语言显示的不同,具体的程序操作本身都是一样的。 
Locale类:表示了特定的地理、整制和文化地区。需要Locale来执行其人物的操作称为语言环境敏感的操作,使用Locale为用户量身定制信息。
构造方法
Locale(String language)
Locale(String language, String country)    eg:Locale locale_CN = new Locale("zh", "CN");
静态方法
Locale.getDefault();//获取当前系统默认的系统语言。
ResourceBundle类
国际化的实现核心在于显示的语言上,通常的做法是将其定义成若干个属性文件(.properties)属性文件中的格式采用键值对的格式进行操作。
Resource Bundle类表示的是一个资源文件的读取操作,所有的资源文件需要使用ResourceBundle进行读取,读取时不需要加后缀。
方法:
getBundle(String baseName)
getBundle(String baseName, Locale locale)
getString(String key)
处理动态文本
使用java.text.MessageFormat类完成。这个类是java.text.Format的子类。

Math和Random类

Math类包含用于执行基本数学运算的方法,如初等指数,对数,平方根和三角函数。
可以直接使用。Math.
import static java.lang.Math.floor;静态导入。
方法举例:static double PI 派的double值。abs(double a)返回绝对值;random返回带正号的double值,大于等于0.0小于1.0;round(double a)返回最近参数并等于某一证书的double值;sqrt(double a) 正确舍入的double值的正平方根

Random用于生成伪随机数流。
nextLong()  返回下一个伪随机数的long值;
nextBoolean()  返回下一个伪随机数的boolean值;
nextDouble()  返回下一个伪随机数
nextFloat()  返回下一个伪随机数
nextInt()  返回下一个伪随机数
nextInt(int n)  返回一个伪随机数,范围在0-n

日期操作类

Date类
表示特定的瞬间,精确到毫秒,也就是程序运行时的当前时间。Date date = new Date();  当前时间

Calendar类
日历类,可以将时间精确到毫秒显示。Calendar c = Calendar.getInstance();    Calendar c = new GregorianCalendar();

DateFormat类及其子类SimpleDateFormat
DateFormat df = new SimpleDateFormat(); 定义一个日期格式。

对象比较器

对两个或多个数据项进行比较,以确定他们是否相等,或确定他们之间的大小关系及排列顺序称为比较。
Arrays.sort方法可以实现对象的排序操作。
Comparable接口:此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法
Comparator接口: 要求自定义类去实现,按照OO原则,如果这个类定义好,不想修改,使用Comparator接口实现强行对某个对象collection进行整体排序的比较

对象的克隆

将一个对象复制一份,称为对象的克隆技术。
Object类中存在一个clone()方法:
protected Object clone() throws CloneNotSupportedException
如果某个类的对象要想被克隆,则对象所在的类必须实现Cloneable接口。此接口没有定义任何方法,是一个标记接口。 

System与Runtime类

System代表系统,系统级的很多属性和控制方法都放在该类内部。位于java.lang包。
内部包含in、out、err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流。
成员方法:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);  源数组,源数组起始位置,目标数组,目标数组起始位置,长度。
public static long currentTimeMillis()该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1日0时0分0秒所差的毫秒数。
public static void exit(int status) 该方法的作用是推出程序,status值为0代表正常退出,非零代表异常退出。使用该方法可以在图形界面编程中实现程序的退出功能。
public static void gc()  请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。
public static String getProperty(String key)  该方法的作用是获得系统中属性名为Key的属性对象的值。
java.version    Java运行时的环境版本
java.home    Java安装目录
os.name    操作系统名称
os.version    操作系统的版本
user.name    用户的账户名称
user.home    用户的主目录
user.dir    用户的当前工作目录

Runtime类:每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接。
Runtime rt = Runtime.getRuntime();
System.out.println("处理器数量:" + rt.availableProcessors() + "个");
System.out.println("Jvm总内存数:" + rt.totalMemory() + "byte");
System.out.println("Jvm空闲内存数:" + rt.freeMemory() + "byte");
System.out.println("Jvm可用最大内存数:" + rt.maxMemory() + "byte");
rt.exec("notepad")  再单独的进程中执行指定的字符串命令

数字处理工具类

BigInteger:可以让超过Integer范围内的数据进行运算
构造方法:public BigInteger(String val)
常用方法:
public BigInteger add(BigInteger val);  //加
public BigInteger subtract(BigInteger val);  //减
public BigInteger multiply(BigInteger val);  //乘
public BigInteger divide(BigInteger val);  //除
public BigInteger remainder(BigInteger val);  //取模%
public BigInteger[] divideAndRemainder(BigInteger val);  //除和取模%

BigDecimal:不可变的、人已经度的有符号十进制数。(为了精确的表示、计算浮点数)
构造方法:public BigDecimal(String val)
常用方法:
public BigDecimal add(BigDecimal augend);  //加
public BigDecimal subtract(BigDecimal subtrahend); //减
public BigDecimal multiply(BigDecimal multiplicand); //乘
public BigDecimal divide(BigDecimal divisor); //除,如果除不尽,则会报错(java.lang.ArithmeticException)
public int scale();  //返回标度,如果数值大于等于零,则标度使小数点后的位数,若小于零,则将该数的非标度值乘10的负scale次幂。例如-3的标度使指非标度值乘以1000

DecimalFormat:为了用最快的速度将数字格式化为指定的格式。
示例:
double pi = 3.1415927;
System.out.println(new DecimalFormat("0").format(pi));  // 取一位整数,结果:3
System.out.println(new DecimalFormat("0.00").format(pi));  // 取一位整数和两位小数,结果:3.14
System.out.println(new DecimalFormat("00.000").format(pi));  // 取两位整数和三位小数,不足补零,结果:03.142
System.out.println(new DecimalFormat("#").format(pi));  // 取所有整数,结果:3
System.out.println(new DecimalFormat("#.##%").format(pi));  // 取两位小数,并以百分比方式计数,结果:314.16%

MD5工具类

Message-Digest Algorithm 5(信息摘要算法)。
MessageDigest md5 = MessageDigest.getInstance("MD5"); 
String newstr = Base64.getEncoder().encodeToString(md5.digest(str.getBytes("utf-8")));

数据结构之二叉树的实现

树是一种重要的非线性数据结构。数据元素(结点)按分支关系组织起来的结构。二叉树(BinaryTree)使每个节点最多有两个子树的有序数。通常子树被称作”左子树“和”右子树“。
二叉树算法的排序规则:选择第一个元素作为根节点;之后如果元素大于根节点放在右子树,小于放在左子树;最后按照中序遍历的方式输出。

public class BinaryTree {

	private Node root;
	
	//添加方法
	public void add(int data){
		if(root==null){
			root = new Node(data);
		}else{
			root.addNode(data);
		}
	}
	//输出方法
	public void print(){
		root.printNode();
	}
	
	private class Node{
		private int data;
		private Node left;
		private Node right;
		public Node(int data){
			this.data = data;
		}
		
		public void addNode(int data){
			if(this.data>data){
				if(this.left==null){
					this.left = new Node(data);
				}else{
					this.left.addNode(data);
				}
			}else{
				if(this.right==null){
					this.right = new Node(data);
				}else{
					this.right.addNode(data);
				}
			}
		}
		
		//中序遍历(左,根,右)
		public void printNode(){
			if(this.left!=null){
				this.left.printNode();
			}
			System.out.print(this.data+"->");
			if(this.right!=null){
				this.right.printNode();
			}
		}
	}
}

jdk1.8新特性(部分)

Lambda表达式
也成为闭包,Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据。Lambda表达式用于简化JAVA中接口式的匿名内部类。被称为函数式接口的概念。函数式接口就是一个具有一个方法的普通接口。像这样的接口可以被隐式转换为lambda表达式。
语法:(参数1, 参数2...) -> {...}
优点:1,代码更简洁,2,不会单独生成class文件

使用方式:
没有参数时使用Lambda表达式
带参数时使用Lambda表达式
代码块中只有一句代码时使用Lambda表达式
代码块中有多句代码时使用Lambda表达式
有返回值的代码块
参数中使用final关键字

package com.vince;

import java.util.Arrays;
import java.util.Comparator;

public class LambdaDemo {
	public static void main(String[] args) {
		
//		IEat ieat = new IEatImpl();
//		ieat.eat();
//		
//		IEat ieat2 = new IEat(){
//			public void eat(){
//				System.out.println("eat bnana");
//			}
//		};
//		ieat2.eat();
		
		
		//lambda表达式:
		//好处:1代码更简洁,2,不会单独生成class文件
//		IEat ieat3 = ()->{System.out.println("eat apple bnana");};
		
		//没有参数时使用
//		IEat ieat3 = ()-> System.out.println("eat apple bnana");
//		ieat3.eat();
		
		//带参数时使用,参数的类型可以省略
//		IEat ieat3 = (thing,name) -> System.out.println("eat..."+thing+"..."+name);
//		ieat3.eat("apple","大冰");
		
		//带参数时使用,参数的类型可以省略,代码块中有多行代码
//		IEat ieat3 = (thing,name) -> {
//			System.out.println("eat..."+thing);
//			System.out.println(name);
//		};
//		ieat3.eat("apple","大冰");
		
		//带返回值的方法
//		IEat ieat3 = (thing,name)->{
//			System.out.println(name+ " eat "+thing);
//			return 10;
//		};
		
		//带返回值 的方法中只有一句实现代码
		IEat ieat3 = (final String thing,final String name)-> thing==null?1:0;
		ieat3.eat("apple", "大冰");
		
		Student[] students = {
				new Student("张三",18),
				new Student("张四",28),
				new Student("张一",15)};
		
//		Arrays.sort(students,new Comparator() {
//			public int compare(Student o1, Student o2) {
//				return o1.getAge()-o2.getAge();
//			};
//		});
		
//		Comparator c = (o1,o2)->o1.getAge()-o2.getAge();
		Arrays.sort(students,(o1,o2)-> o1.getAge()-o2.getAge());
		System.out.println(Arrays.toString(students));
		
		IEat.method();
	}
}


//只有一个抽象方法的接口
interface IEat{
	public int eat(final String thing,final String name);
	public default void print(){
		System.out.println("print test");
	}
	public static void method(){
		System.out.println("static method..");
	}
}

//class IEatImpl implements IEat{
//	public void eat(String thing){
//		System.out.println("eat--"+thing);
//	}
//}

接口中的默认方法和静态方法

interface A{
    public default void print(){}
    public static void method(){} 
}
默认方法与静态方法并不影响函数式接口的契约,可以任意使用。

你可能感兴趣的:(学习过程)