一、 java8增强的包装类
包装类的作用:Java语言中,一切都是对象,但是有例外:8个基本数据类型不是对象,因此在很多时候非常不方便。
为此,Java提供为8个基本类型提供了对应的包装类:
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean
自动装箱: 当我们把一个基本类型的值(20),赋值给引用变量时候,系统可以自动将它“包装”为相应的包装类的实例
程序需要对象时,如果给的只是一个基本类型的值,系统会将它自动装箱为包装类的实例
达到的效果:有了自动装箱之后,基本类型的值可以当成对象用——其实是【假相】。
自动拆箱: 当我们需要一个基本类型的值时,但实际上传入的包装类的对象。系统会自动把对象“剥”开,得到它的值。
达到的效果:有了自动拆箱之后,包装类的对象可当成基本类型的值用——其实是【假相】。
包装类额外功能:
将字符串,转换相应的基本类型的值。
每个包装类,都提供了static修饰的parseXxx(String),用于将字符串转为相应的基本类型的值。 valueOf(String)
所有基本类型的值,都可通过 + "" 转换为String。
String intStr=5+"";
public class Primitive2String
{
public static void main(String[] args)
{
String intStr = "123";
// 把一个特定字符串转换成int变量
int it1 = Integer.parseInt(intStr);
int it2 = Integer.valueOf(intStr);
System.out.println(it2);
String floatStr = "4.56";
// 把一个特定字符串转换成float变量
float ft1 = Float.parseFloat(floatStr);
float ft2 = Float.valueOf(floatStr);
System.out.println(ft2);
// 把一个float变量转换成String变量
String ftStr = String.valueOf(2.345f);
System.out.println(ftStr);
// 把一个double变量转换成String变量
String dbStr = String.valueOf(3.344);
System.out.println(dbStr);
// 把一个boolean变量转换成String变量
String boolStr = String.valueOf(true);
System.out.println(boolStr.toUpperCase());
}
}
基本类型和字符串之间的转换方法
- 字符串转换成基本类型
int i=Integer.parseInt(str);
或者
int i=new Integer(str); - 基本类型转换成字符串
String flStr=String.valueOf(float变量);
或者
String flStr=float变量+“”;
包装类比较大小
- 如果直接给integer数字,如果数字在-128-127之间,则两个数字相等,包装类相等。
- 如果用new integer(2)赋值,则必须两个包装类指向同一个对象才相等。
- 有一个compare(a,b)可以直接用来比较包装类大小.
a>b 返回1
a=b 返回0
a<b 返回-1
二、处理对象
打印对象和toString方法
toString方法
System.out.println(p);
System.out.println(p.toString());两者效果一样
toString () 是Object类里面的一个实例方法,所有对象都具有这个方法。
系统自带的tostring“类名+@+hashcode”
可以自己重写这个方法。
==和equals方法
“==”
- 对于基本类型的数值类型(包括char),只要两个变量的值相等,返回true。
- 对于引用类型,必须指向同一个对象,才返回true。
注意:“==“不能用于比较没有父子关系的两个对象。
”hello“直接量和new String (“hello”)区别
直接使用hello这种可以在编译时候计算出来的字符串值,jvm会用常量池来管理。jvm会保证相同的字符串直接量只有一个,不会产生多个副本。
第二种,jvm会先用常量池来管理直接量,再调用string构造器来创建 i 个新的string对象。新创建的对象保存在堆中
equals()
Java首先提供了==来判断两个引用变量是否相等。
== 要求两个引用变量必须指向同一个对象才算相等。
但equals()就不同了,也用于判断两个对象是否相等。
默认情况下,你调用的equals()方法是Object提供了,Object所提供的equals()方法的判断标准,与==的判断标准是完全一样。
可以这样说, Object提供的equals()几乎不能直接用于判断两个对象是否相等。
如果你要根据自己的标准来判断两个对象是否相等,就需要重写equals()方法。
Java在提供了String类,已经重写equals()方法,
标准是:只要两个字符串包含相同的字符序列,这两个字符串通过equals()比较就会返回true.
因此:如果以后我们要判断两个String所包含的字符序列是否相等,应该通过a.equals(b)进行比较。
三、 类成员
理解类成员
类成员就是用static修饰的成员变量,方法,初始化块,内部类的统称。能通过对象访问这些类成员。即使对象是null 。类成员不能访问实例成员,因为类成员的作用域更大,可能类成员还存在,但是成员变量已经不存在了,所以不能够访问实例成员。
单例类
一个类始终只能创建一个实例,称为单例类。
如果不想让别的类轻易的创建该类的对象,就需要把类的构造函数设置成private,但是这个时候就需要有一个方法来创建一个对象,由于使用这个方法的时候还没有对象,因此需要给方法是public static的。
同时。为了满足只能创建一个对象,则必须把已经创建的对象保存起来,每次创建对象之前都检查一下是否只有一个对象。由于 存储对象的成员变量需要通过上面的static方法调用,因此需要设置为static。
class Singleton
{
// 使用一个类变量来缓存曾经创建的实例
private static Singleton instance;
// 将构造器使用private修饰,隐藏该构造器
private Singleton(){}
// 提供一个静态方法,用于返回Singleton实例
// 该方法可以加入自定义的控制,保证只产生一个Singleton对象
public static Singleton getInstance()
{
// 如果instance为null,表明还不曾创建Singleton对象
// 如果instance不为null,则表明已经创建了Singleton对象,
// 将不会重新创建新的实例
if (instance == null)
{
// 创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
public class SingletonTest
{
public static void main(String[] args)
{
// 创建Singleton对象不能通过构造器,
// 只能通过getInstance方法来得到实例
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // 将输出true
}
}
保证只产生一个实例,两次产生的Singleton对象实际是同一个对象
四、 final修饰符
final可修饰类、方法、变量(一切变量, 类变量、实例变量、局部变量)。
final与abstract是互斥,永远不可能同时出现
当final修饰变量时,总则是:该变量被赋值之后,再也不能重新赋值。
final成员变量
类变量:必须指定初始化,在静态初始化块或者声明该变量时初始化
实例变量:在普通初始化块,或者声明该变量时候,或者构造器。如果已经在普通初始化块赋值,则不可以再在构造器中赋值。
注意⚠:普通方法不能访问final修饰的成员变量。
final修饰的成员变量必须程序员自己显示初始化,系统不会默认赋值。
final局部变量
因为系统不会给局部变量初始化,因此在用final修饰的局部变量不一定非由程序员显示初始化。
不能对final修饰的形参赋值
final修饰基本类型变量和引用类型变量的区别
final修饰基本类型变量,就不能更改值了。
如果是引用类型,引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容
class Person
{
private int age;
public Person(){}
// 有参数的构造器
public Person(int age)
{
this.age = age;
}
// 省略age的setter和getter方法
// age的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
}
public class FinalReferenceTest
{
public static void main(String[] args)
{
// final修饰数组变量,iArr是一个引用变量
final int[] iArr = {5, 6, 12, 9};
System.out.println(Arrays.toString(iArr));
// 对数组元素进行排序,合法
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
// 对数组元素赋值,合法
iArr[2] = -8;
System.out.println(Arrays.toString(iArr));
// 下面语句对iArr重新赋值,非法
// iArr = null;
// final修饰Person变量,p是一个引用变量
final Person p = new Person(45);
// 改变Person对象的age实例变量,合法
p.setAge(23);
System.out.println(p.getAge());
// 下面语句对p重新赋值,非法
// p = null;
}
}
可执行“宏替换”的final变量
变量满足三个条件,则变量相当于一个直接量
(1)final修饰
(2)在定义的时候指定了初始值(只有在定义的时候赋值才有这种效果)
(3)值可以在编译的时候确定下来
String类:
只要程序中用到了任何“字符串直接量”,该直接量就会被放入一个“房间”(池、Pool),下次如果再用到该直接量时,就直接从池中取出来用。
final 方法
final方法不能够被重写。如果父类的方法不希望子类重写,只要加上final 就好。
但是父类的private 是不会被子类继承的,因此也不会有重写这个说法。因此如果父类的private方法被final了,子类仍然可以写一个一样的方法相同方法名,相同形参列表,相同返回值。
public class PrivateFinalMethodTest
{
private final void test(){}
}
class Sub extends PrivateFinalMethodTest
{
// 下面方法定义将不会出现问题
public void test(){}
}
final类
不能有子类的类,不可被 继承
不可变类
不可变类是创建该类的实例后,实例变量不能够更改。
创建自定义的不可变类方法:
1,类的成员变量用private和final修饰
2,提供带参数的构造函数,用于根据传入参数来初始化类的成员变量
3,该类仅有getter
4,重新定义equals()和hashcode()
public class Address
{
private final String detail;
private final String postCode;
// 在构造器里初始化两个实例变量
public Address()
{
this.detail = "";
this.postCode = "";
}
public Address(String detail , String postCode)
{
this.detail = detail;
this.postCode = postCode;
}
// 仅为两个实例变量提供getter方法
public String getDetail()
{
return this.detail;
}
public String getPostCode()
{
return this.postCode;
}
//重写equals()方法,判断两个对象是否相等。
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if(obj != null && obj.getClass() == Address.class)
{
Address ad = (Address)obj;
// 当detail和postCode相等时,可认为两个Address对象相等。
if (this.getDetail().equals(ad.getDetail())
&& this.getPostCode().equals(ad.getPostCode()))
{
return true;
}
}
return false;
}
public int hashCode()
{
return detail.hashCode() + postCode.hashCode() * 31;
}
}
当创建不可变类的时候,如果成员变量里面有引用类型,则很可能创建出一个可变类,因为成员变量的内容可以更改。必须采用一些其他的方法,才能创建真正的不可变类。
缓存实例的不可变类
缓存实例的不可变类,是因为有的时候一个对象的某个成员被多次引用,为了节省开销,可以把它缓存起来。下边是把它缓存在数组里面,如果缓存里面已经有了,就直接返回实例,如果没有,就新建实例加进去。
class CacheImmutale
{
private static int MAX_SIZE = 10;
// 使用数组来缓存已有的实例
private static CacheImmutale[] cache
= new CacheImmutale[MAX_SIZE];
// 记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
private static int pos = 0;
private final String name;
private CacheImmutale(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public static CacheImmutale valueOf(String name)
{
// 遍历已缓存的对象,
for (int i = 0 ; i < MAX_SIZE; i++)
{
// 如果已有相同实例,直接返回该缓存的实例
if (cache[i] != null
&& cache[i].getName().equals(name))
{
return cache[i];
}
}
// 如果缓存池已满
if (pos == MAX_SIZE)
{
// 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置。
cache[0] = new CacheImmutale(name);
// 把pos设为1
pos = 1;
}
else
{
// 把新创建的对象缓存起来,pos加1
cache[pos++] = new CacheImmutale(name);
}
return cache[pos - 1];
}
public boolean equals(Object obj)
{
if(this == obj)
{
return true;
}
if (obj != null && obj.getClass() == CacheImmutale.class)
{
CacheImmutale ci = (CacheImmutale)obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode()
{
return name.hashCode();
}
}
public class CacheImmutaleTest
{
public static void main(String[] args)
{
CacheImmutale c1 = CacheImmutale.valueOf("hello");
CacheImmutale c2 = CacheImmutale.valueOf("hello");
// 下面代码将输出true
System.out.println(c1 == c2);
}
}
五、抽象类
抽象方法是只有方法的签名,没有方法的实现
抽象方法和抽象类
抽象方法和抽象类必须使用abstract修饰,有抽象方法的类一定是抽象类。
规则:
- 抽象方法不能有方法体
- 抽象类不能够被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例
- 抽象类的构造函数不能够用来创造实例,主要用于被子类调用
- 含有抽象方法的类(包括直接定义了一个抽象方法;继承了一个抽象父类,但是没有完全实例化父类包含的抽象类;或是实现了一个接口,但是没有完全实例化接口包含的抽象方法)只能被定义成抽象类
public abstract class Shape
{
{
System.out.println("执行Shape的初始化块...");
}
private String color;
// 定义一个计算周长的抽象方法
public abstract double calPerimeter();
// 定义一个返回形状的抽象方法
public abstract String getType();
// 定义Shape的构造器,该构造器并不是用于创建Shape对象,
// 而是用于被子类调用
public Shape(){}
public Shape(String color)
{
System.out.println("执行Shape的构造器...");
this.color = color;
}
// 省略color的setter和getter方法
public void setColor(String color)
{
this.color = color;
}
public String getColor()
{
return this.color;
}
}
抽象类不能用于创建实例,只能当作父类被其他子类调用
abstract与final不能同时出现:abstract类表示只能被继承,但是final类不能被继承。
abstract和static一般不能同时修饰方法:static修饰的方法表示属于类的,可以通过类来访问。但是如果同时也是abstract的话,则没有方法体。这就没办法调用。(但可以同时修饰内部类)
abstract和private不能同时修饰方法:private修饰的方法是不会被继承的。但是abstract需要继承。
抽象类的作用
正是用于被子类继承,作为模板模式设计,作为子类通用模板
六、 java8改进的接口
将抽象类“抽象”到极致,只包含抽象方法。就是接口。
接口的概念
接口定义的是多个类共同的公共行为规范,接口里通常是定义一组公共方法。
接口不提供任何实现,体现的是实现和规范相分离的设计哲学。
java8中接口的定义
[修饰符] interface 接口名 extends 父接口1,父接口2...
{
0~N个field —— 只能是public static final修饰的field。
0~N个方法 —— 只能是public abstract方法。
0~N个内部类|接口|枚举 —— 只能是public static修饰。
}
修饰符:public 或者省略,省略是默认default
接口名一般用形容词。接口名与类名的命名规范是相同:每个单词的首字母大写。
由于接口定义的是一种规范,因此接口没有构造器和初始化块,接口可以包含成员变量(静态常量),静态方法和抽象方法以及默认方法。都必须是public
- 静态常量:无论是否有修饰符,都是public static final的,需要在定义的时候指定默认值。可以跨包访问,但是因为是final,不能修改值。
- 接口里面的普通方法只能是public的抽象abstract方法
- 在接口定义默认方法,需要使用default修饰(默认都是public修饰,不能static修饰)
- 在接口定义类方法,需要使用static(默认都是public ,不能用default修饰)
- java里面最多定义一个public的接口,如果有public的接口,则主文件名和接口名相同
-
package lee; /** * Description: * 网站: <a href="http://www.crazyit.org">疯狂Java联盟</a><br> * Copyright (C), 2001-2018, Yeeku.H.Lee<br> * This program is protected by copyright laws.<br> * Program Name:<br> * Date:<br> * @author Yeeku.H.Lee kongyeeku@163.com * @version 1.0 */ public interface Output { // 接口里定义的成员变量只能是常量 int MAX_CACHE_LINE = 50; // 接口里定义的普通方法只能是public的抽象方法 void out(); void getData(String msg); // 在接口中定义默认方法,需要使用default修饰 default void print(String... msgs) { for (String msg : msgs) { System.out.println(msg); } } // 在接口中定义默认方法,需要使用default修饰 default void test() { System.out.println("默认的test()方法"); } // 在接口中定义类方法,需要使用static修饰 static String staticTest() { return "接口里的类方法"; } // 定义私有方法 private void foo() { System.out.println("foo私有方法"); } // 定义私有静态方法 private static void bar() { System.out.println("bar私有静态方法"); } }
接口的继承
支持多继承,一个接口可以有多个直接父接口,以逗号分格
使用接口
一个类在继承另一个类的同时,还可以实现多个接口。
[修饰符] class 类名 extends 父类 implements 接口1,接口2.....
{
类体部分
}
接口用途:
1.定义变量,也可用于强制类型转换
2.调用接口中定义的常量
3.被其它类实现
implements实现多个接口。如果一个类继承了一个接口,就必须把里面的抽象方法都实现,否则就必须定义成抽象类。
实现接口的方法时必须使用public
模拟多继承:接口名 引用变量名=new 类(初始化参数),类就可以访问接口的方法以及自己的方法。类的方法就变的很多。
import lee.Output;
// 定义一个Product接口
interface Product
{
int getProduceTime();
}
// 让Printer类实现Output和Product接口
public class Printer implements Output , Product
{
private String[] printData
= new String[MAX_CACHE_LINE];
// 用以记录当前需打印的作业数
private int dataNum = 0;
public void out()
{
// 只要还有作业,继续打印
while(dataNum > 0)
{
System.out.println("打印机打印:" + printData[0]);
// 把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData , 1
, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if (dataNum >= MAX_CACHE_LINE)
{
System.out.println("输出队列已满,添加失败");
}
else
{
// 把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++] = msg;
}
}
public int getProduceTime()
{
return 45;
}
public static void main(String[] args)
{
// 创建一个Printer对象,当成Output使用
Output o = new Printer();
o.getData("轻量级Java EE企业应用实战");
o.getData("疯狂Java讲义");
o.out();
o.getData("疯狂Android讲义");
o.getData("疯狂Ajax讲义");
o.out();
// 调用Output接口中定义的默认方法
o.print("孙悟空" , "猪八戒" , "白骨精");
o.test();
// 创建一个Printer对象,当成Product使用
Product p = new Printer();
System.out.println(p.getProduceTime());
// 所有接口类型的引用变量都可直接赋给Object类型的变量
Object obj = p;
}
}
Printer类实现了Output接口和Product接口,因此Printer对象既可直接赋给Output变量,也可直接赋给Prouduct变量,
接口和抽象类
接口:体现一种规范,对于接口的实现者,接口定义了必须实现那些服务;对于接口的调用者,规定了调用者可以调用哪些方法。
抽象类:体现一种模版的设计,他是没有设计完的一个类,需要子类补充将它完成。
除此之外,接口和抽象类在用法上也存在如下差别。
➢接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
➢接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
➢接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
➢接口里不能包含初始化块:但抽象类则完全可以包含初始化块。
➢一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。”
面向接口编程
- 简单的工厂模式
让Computer类组合一个Output类型的对象,将Computer类与Printer类分离,便于系统重构,
import lee.Output;
public class Computer
{
private Output out;
public Computer(Output out)
{
this.out = out;
}
// 定义一个模拟获取字符串输入的方法
public void keyIn(String msg)
{
out.getData(msg);
}
// 定义一个模拟打印的方法
public void print()
{
out.out();
}
}
2.命令模式
定义一个接口,接口里面定义一个抽象的方法,作用在一个数组上。然后实例化这个接口,可以实例化多个,每个都是作用在数组上的一种方法,
???????????
七、内部类
定义在其他类内部的类叫做内部类。
包含内部类的类叫做外部类。
内部类的作用:
- 提供了更好的封装性,不允许同一个包中的其他类访问。
- 内部类可以直接访问外部类的私有数据。因为内部类可以当作外部类成员。外部类不可以访问内部类的实现细节
- 匿名内部类适合用于创建只需要一次使用的类。
内部类外部类区别: - 内部类比外部类多3个修饰符,private protected static
- 非静态内部类不能有静态成员
非静态内部类
在外部类里面使用非静态内部类时,和使用普通的类没有什么区别。
非静态内部类可以访问外部类的pirvate成员,因为非静态内部类的对象里面,保存了一个外部类对象的引用。
外部类成员变量,内部类成员变量,内部类里面方法的局部变量可以同名,用this区分。
外部类不能够访问非晶态内部类的成员,必须创建一个对象来调用访问实例成员才行。new inner(),,,因为外部类存在的时候,内部类不一定存在,但是内部类存在,外部类一定存在。
不允许在外部类的静态成员中直接使用非静态内部类
public class Outer
{
private int outProp = 9;
class Inner
{
private int inProp = 5;
public void acessOuterProp()
{
// 非静态内部类可以直接访问外部类的private成员变量
System.out.println("外部类的outProp值:"
+ outProp);
}
}
public void accessInnerProp()
{
// 外部类不能直接访问非静态内部类的实例变量,
// 下面代码出现编译错误
// System.out.println("内部类的inProp值:" + inProp);
// 如需访问内部类的实例变量,必须显式创建内部类对象
System.out.println("内部类的inProp值:"
+ new Inner().inProp);
}
public static void main(String[] args)
{
// 执行下面代码,只创建了外部类对象,还未创建内部类对象
Outer out = new Outer(); //①
out.accessInnerProp();
}
}
静态内部类
用static修饰的内部类叫做静态内部类。这个内部类属于外部类本身,不属于外部类的任何一个对象。
外部类不能够用statc修饰,因为外部类的上一级是包,所以没有类的概念,但是内部类的上一层是外部类,所以可以用static修饰。
静态内部类可以有静态成员和非静态成员,静态内部类不能够访问外部类的实例成员,只能访问类成员。(因为静态内部类里面只有外部类的引用,没有外部类对象的引用)
外部类依旧不能访问内部类的成员,但是可以通过类名或者对象访问内部类成员对象。java允许定义接口内部类,默认是public static修饰。也就是说,接口的内部类一定是静态内部类。
public class AccessStaticInnerClass
{
static class StaticInnerClass
{
private static int prop1 = 5;
private int prop2 = 9;
}
public void accessInnerProp()
{
// System.out.println(prop1);
// 上面代码出现错误,应改为如下形式:
// 通过类名访问静态内部类的类成员
System.out.println(StaticInnerClass.prop1);
// System.out.println(prop2);
// 上面代码出现错误,应改为如下形式:
// 通过实例访问静态内部类的实例成员
System.out.println(new StaticInnerClass().prop2);
}
}
使用内部类
1.在外部类内部使用内部类
基本上与平常使用普通类没有区别。唯一的区别是不要在外部类的静态成员中使用非静态内部类。
2.在外部类以外使用非静态内部类
在外部类以外的地方定义内部类变量的语法:
outclassname.innerclassname name;
创建非静态内部类对象(非静态内部类的构造器必须用外部类对象调用)
outerInstance.new InnerConstructor()
class Out
{
// 定义一个内部类,不使用访问控制符,
// 即只有同一个包中其他类可访问该内部类
class In
{
public In(String msg)
{
System.out.println(msg);
}
}
}
public class CreateInnerInstance
{
public static void main(String[] args)
{
Out.In in = new Out().new In("测试信息");
/*
上面代码可改为如下三行代码:
使用OutterClass.InnerClass的形式定义内部类变量
Out.In in;
创建外部类实例,非静态内部类实例将寄存在该实例中
Out out = new Out();
通过外部类实例和new来调用内部类构造器创建非静态内部类实例
in = out.new In("测试信息");
*/
}
}
1.创建非静态内部类的子类
package demo6;
public class SubClass extends Out.In {
public SubClass(Out out)
{
out.super("hello");
}
}
- 在外部类以外使用静态内部类
new outclass.innerConstruction
可以看出无论是静态内部类还是非静态内部类,声明变量的方法都是一样的。区别在于创建内部类对象。优先考虑静态内部类。
局部内部类
放在方法里面的内部类
一般不用
java8改进的匿名内部类
new 父类构造器(实参列表) | 实现接口()
{
// 匿名内部类的 类体部分
};
匿名内部类适合创建只需要一次使用的类。创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。
匿名内部类必须继承一个父类,或者实现一个接口,但是最多只能是一个。
匿名内部类的两条规则:
- 不能是抽象类,因为抽象类不能被实例化,但是匿名内部类创建的时候就要创建对象
- 不能定义构造器,因为匿名内部类没有类名。
最常用的创建匿名内部类是需要创建某个接口类型的对象。
局部变量被匿名内部类访问,局部变量相当于自动加了final修饰。因此不能够再被修改。interface Product { public double getPrice(); public String getName(); } public class AnonymousTest { public void test(Product p) { System.out.println("购买了一个" + p.getName() + ",花掉了" + p.getPrice()); } public static void main(String[] args) { AnonymousTest ta = new AnonymousTest(); // 调用test()方法时,需要传入一个Product参数, // 此处传入其匿名实现类的实例 ta.test(new Product() { public double getPrice() { return 567.8; } public String getName() { return "AGP显卡"; } }); } }
八、java8新增的lambda表达式
lambda表达式入门
public class CommandTest2
{
public static void main(String[] args)
{
ProcessArray pa = new ProcessArray();
int[] array = {3, -4, 6, 4};
// 处理数组,具体处理行为取决于匿名内部类
pa.process(array , (int[] target)->{
int sum = 0;
for (int tmp : target )
{
sum += tmp;
}
System.out.println("数组元素的总和是:" + sum);
});
}
}
从上面程序中的粗体字代码可以看出,这段粗体字代码与创建匿名内部类时需要实现的pocssintarget()方法完全相同,只是不需要new XxxO{} 这种烦琐的代码,不需要指出重写的方法名字,也不需要给出重写的方法的返回值类型一只 要给出重写的方法括号以及括号里的形参列表即可。
一从上面介绍可以看出,当使用Lambda表达式代替匿名内部类创建对象时,Lambda 表达式的代码块将会代替实现抽象方法的方法体,Lambda表达式就相当一-个匿名方法。
从上面语法格式可以看出,Lambda 表达式的主要作用就是代替匿名内部类的烦琐语法。它由三部分组成。
➢形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆
括号也可以省略。
➢箭头(->)。 必须通过英文中画线和大于符号组成。
➢代码块。如果代码块只包含一条语句,Lambdla表达式允许省略代码块的花括号,那么这条语向就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda 表达式自动返回这条语句的值
lambda表达式和函数式接口
interface FkTest
{
void run();
}
public class LambdaTest
{
public static void main(String[] args)
{
// Runnable接口中只包含一个无参数的方法
// Lambda表达式代表的匿名方法实现了Runnable接口中唯一的、无参数的方法
// 因此下面的Lambda表达式创建了一个Runnable对象
Runnable r = () -> {
for(int i = 0 ; i < 100 ; i ++)
{
System.out.println();
}
};
// // 下面代码报错: 不兼容的类型: Object不是函数接口
// Object obj = () -> {
// for(int i = 0 ; i < 100 ; i ++)
// {
// System.out.println();
// }
// };
//下面对表达式强制转换,确定表达式的目标类型为Runnable
Object obj1 = (Runnable)() -> {
for(int i = 0 ; i < 100 ; i ++)
{
System.out.println();
}
};
// 同样的Lambda表达式可以被当成不同的目标类型,唯一的要求是:
// Lambda表达式的形参列表与函数式接口中唯一的抽象方法的形参列表相同
Object obj2 = (FkTest)() -> {
for(int i = 0 ; i < 100 ; i ++)
{
System.out.println();
}
};
}
}
lambda表达式的目标类型必须是函数式接口
函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法,但只能有一个抽象方法。
java8为函数式接口加了@FunctionalInterface注解 用于告诉编译器更严格的检查
方法引用和构造器引用
如果代码块只有一行代码,则可以在lambda表达式中使用方法引用和构造引用。
引用类方法 类名::类方法
引用特定对象的实力方法 特定对象::实例方法
引用某类对象的实例方法 类名::实例方法
引用构造器 类名::new
lambda表达式和匿名内部类的联系和区别
lambda表达式是匿名内部类的一种简化。
相同点:
都可以直接访问“effectively final”的局部变量,以及外部类的成员变量
所创建的对象可以直接调用从接口中继承的默认方法
区别
匿名内部类可以为任意接口创建实例,而lambda表达式必须是函数式接口
匿名内部类可以为抽象类甚至普通类创建实例。
匿名内部类的方法体可以调用接口中定义的默认方法,但是lambda不可以,它只有对象可以调用。
九、枚举类
枚举类是指实例有限而且固定的类
枚举类入门
枚举类是一种特殊的类,可以有自己的成员变量,方法,可以实现一个或者多个接口。
1.默认继承java.lang.Enum类,不能显式继承其他父类。
2.使用Enum定义,非抽象的枚举类默认是final修饰,不能派生子类
3.构造器为private
4.所有实例必须在第一行显式列出,系统默认加上public static final
如果需要使用某个实例,用EnumClass.variable
所有的枚举类都继承了java lng Emm类,所以枚举类可以直核使用java. lang Enum类中所包含的方法。java lang Enum类中提供了如下几个方法。
➢int compareTo(E o):该方法用于与指定枚举对象比较顺序,同一个枚举实例只能与相同类型的枚举实例进行比较。如果该枚举对象位于指定枚举对象之后,则返回正整数:如果该枚举对象位于指定枚举对象之前,则返回负整数,否则返回零。
➢String name():返回此枚举实例的名称,这个名称就是定义枚举类时列出的所有枚举值之一。与此方法相比,大多数程序员应该优先考虑使用tString()方法, 因为toSring()方法返 回更加用户友好的名称。
➢intordinal():返回枚举值在枚举类中的索引值(就是枚举值在枚举声明中的位置,第一个枚举值的索引值为零)。
➢String toString():返回枚举常量的名称,与name方法相似,但toString方法更常用。
➢public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name):这是一个静态方法,用于返回指定枚举类中指定名称的枚举值。名称必须与在该枚举类中声明枚举值时所用的标识符完全匹配,不允许使用额外的空白字符。
正如前面看到的,当程序使用Sytemoutprintn(s)语句来打印枚举值时,实际上输出的是该枚举值的toString0方法, 也就是输出该枚举值的名字。
枚举类的成员变量,方法和构造器
枚举类的成员变量最好都使用private final修饰
如果构造函数有参数,则在第一行列出实例的时候,要写上参数。
枚举类的实例只能是枚举值,不能随意通过new来创建。???
6.9.4 实现接口的枚举类
与普通类完全一样,也需要实现该接口所包含的方法
如果不同的枚举值想在调用一个方法时呈现不同的行为方式,则可以让每个枚举值分别实现该方法,这个时候,不是在创建枚举类的实例,而是创建匿名子类的实例。
6.9.5 包含抽象方法的枚举类
每个枚举值都必须为抽象方法提供实现,否则报错。
6.10 对象和垃圾回收
6.10.1 对象在内存中的状态
可达状态
可恢复状态
不可达状态
6.10.2 强制垃圾回收
System.gc()
Runtime.getRuntime().gc()
6.10.3 finalize方法
6.11 修饰符的适用范围
6.12 使用JAR文件