单例模式保证来到对象在内存中保证唯一
public class Singleton{ private static final Singleton singleObject = new Singleton(); private Singleton(){} public static getSingleton(){ return singleObject; } }
饿汉模式在第一次引用该类的时候就创建对象实例。
饱汉模式
public class Singleton{ private static Singleton singleObject = null; private Singleton(){} public static getSingleton(){ if(singleObject == null){ singleObject = new Singleton; } return singleObject; } }
双重锁模式
//这种方式只考虑到线程安全,但每次调用getSingleton()方法 //都必须在synchornized进行排队 public class Singleton{ private static Singleton singleObject = null; private Singleton(){} public static getSingleton(){ synchronized(Singleton.class){ if(singleObject == null){ singleObject = new Singleton; }
} return singleObject; } }
改进之后的
//先进行判断是否已存在对象,如无进入线程等待 //已存在对象就只需要返回,不需要线程等待 public class Singleton{ private static volatile Singleton singleObject = null; private Singleton(){} public static getSingleton(){ if(singleObject == null){ synchronized(Singleton.class){ if(singleObject == null){ singleObject = new Singleton; } } return singleObject; } } }
几种模式的比较:
1、饿汉模式是线程安全的,因为实例对象在类加载过程中就会被创建,在getInstance()方法中只是直接返回对象引用。
好处:无需关注线程安全问题。
缺点:如果在一个大环境下使用了过多的饿汉单例,则会生产出过多的实例对象。
2、饱汉模式不是线程安全的,因为是在需要的时候才会产生实例对象,生产之前会判断对象引用是否为空,这里,如果多个线程同时进入判断,就会生成多个实例对象,这是不符合单例的思想的。所以饱汉模式为了保证线程安全,就用synchronized关键字标识了方法。之所以被称为“饱汉”,因为它很饱,不急着生产实例,在需要的时候才会生产。
好处:延时加载,用的时候才会生产对象。
缺点:需要保证同步,付出效率的代价。
3、双重锁模式,是饱汉模式的优化,进行双重判断,当已经创建过实例对象后就无需加锁,提高效率。
枚举写法单例模式
public enum EnumSingleton{ singleObject; private String name; public String getName(){ return name; } public void setName(String name){ this.name = name; } }
Lambda表达式
//创建线程 Thread td = new Thread(new Runnable){ public void run(){ System.out.println("---"); } }; td.start(); //表达式 Thread td = new Thread(() -> System.out.println("---")); td.start(); //排序 List<String> list = Arrays.asList(new String[]{"a", "b", "c"}); Collections.sort(list, new Comparator<String>){ public int compare(String str1, String str2){ return str1.conpareTo(str2); } }); //表达式 Collections.sort(list, (str1, str2) -> str1.compareTo(str2));
当使用Lambda表达式替代内部类创建对象时,Lambda表示的代码块将会代替实现抽象方法的方法体,Lambda表达式就相当于一个匿名方法。
形参列表:形参列表允许省略形参类型。如果形参列表只有一个参数,甚至连形参列表的圆括号都可以省略。
箭头 ->必须通过英文中线和大于符号组成
代码块,如果代码块只有一条语句,允许省略代码块的花括号。Lambada代码块只有一条return语句,甚至可以省略return关键字。Lambda需要返回值,儿它的代码块省略了return关键字,会自动返回这条语句的值。
public class LambdaClass2{ public void Method1(InterfaceClass1 inClass1){ //System.out.println(inClass1); inClass1.InterfaceClass1Method(); } public void Method2(InterfaceClass2 inClass2){ inClass2.InterfaceClass2Method("适合出游"); } public void Method3(InterfaceClass3 inClass3){ //inClass3.InterfaceClass3Method(5, 3); System.out.println(inClass3.InterfaceClass3Method(5, 3)); } public static void main(String[] args){ LambdaClass2 lc = new LambdaClass2(); //如果只有一个语句可以省略花括号 lc.Method1(() -> System.out.println("这是一个苹果")); //Lambda只有一个形参,可以省略圆括号 lc.Method2(str -> { System.out.println("天气不错" + str); System.out.println("雨夹雪"); }); //代码块中只有一条语句,即使该表达式需要返回值,也可以省略return关键字 lc.Method3((a, b) -> a + b); } } interface InterfaceClass1{ void InterfaceClass1Method(); } interface InterfaceClass2{ void InterfaceClass2Method(String str); } interface InterfaceClass3{ int InterfaceClass3Method(int a, int b); }
Lambda表达式也被称为目标类型,Lambda表达式的目标类型必须是函数式接口。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
由于Lambda表达式的结果被当成对象,因此外圈可以使用Ladam表达式进行赋值。
//Runnable接口只包含一个无参方法 //Lambda表达式代表的匿名方法实现了Runnable接口中唯一的、无参的方法 Runnable r = () -> { //方法体 };
Lambda表达式实现的是匿名方法,因此只能实现特定函数式接口中的唯一方法,意味着Lambda有两个限制:1、Lambda表达式的目标累心必须是明确的函数式接口; 2、Lambda表达式只能为函数式接口创建对象,Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口创建对象。
public class LambdaClass3{ public void method(interfaceClass1 ifc){ System.out.println(ifc); ifc.test(); } public static void main(String[] args){ LambdaClass3 lc = new LambdaClass3(); //Lambda只能实现一个方法 lc.method(() -> { System.out.println("test方法"); }); } } interface interfaceClass1{ void test(); //函数式接口只能有一个抽象方法,但可以有类方法和默认方法 public static void test2(){ System.out.println("接口的类方法"); } }
为了保证Lambda表达式的目标类型是一个明确的函数值接口,可以有三种方式:1、将Lambda表达式赋值给函数式接口类型的变量; 2、将Lambda表示作为函数式接口类型的参数传给某个方法; 3使用函数式接口对Lambda表示进行强制类型转换。
Object obj = () -> { // ... }
上面代码会报错,因为Object不是函数式接口,所以需要强制类型转换
Object obj =(Runnable)() -> { // ... }
Lambda表达式的目标类型完全可能是变化的唯一的要求是Lambda表达式实现的匿名方法和目标类型中唯一的抽象方法有相同的形参列表。
上面Runnable是无参的,FKTest也是无参的,所以可以直接强制类型转换成FKTest类。
interface FKTest{ void run(); } Object obj = (FKTest) -> { //方法体 }
同样的Lambda表达可以被当成不同的目标类型,唯一要求是函数式接口唯一的抽象方法的形参列表相同。
如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。
引用类方法 类名 :: 方法 函数式接口中被实现方法的全部参数传给该类作为参数 (a, b, c...) -> 类名.类方法(a, b, c...)
interface Converter{ Integer convert(String from); } public class LambdaClass4{ public static void main(String[] args){ //Converter converter1 = from -> Integer.valueOf(from); Converter converter1 = Integer :: valueOf; Integer val = converter1.convert("99"); System.out.println(val); } }
引用特定对象的实例方法 特定对象 :: 实例方法 函数式接口被实现方法的全部参数传给该方法作为参数 (a, b, c...) -> 特定对象.实例方法(a, b,..)
interface Converter{ Integer convert(String from); } class Test{ public Integer convert(String from){ return Integer.valueOf(from); } } public class LambdaClass4{ public static void main(String[] args){ Test t = new Test(); //引用方法 //方式一:Lambda表达式,这里是调用其他类方法来处理 //Converter converter1 = from -> t.convert(from); //方式二 Converter converter1 = t :: convert; Integer val = converter1.convert("99"); System.out.println(val); } }
引用某类对象的实例方法 类名 :: 实例方法 函数式接口中被实现方法的第一个参数作为调用者后面的参数全部传给该方法作为参数 (a, b...) -> a.实例方法(a, b...)
interface Converter{ String convert(String from, int i, int j); } public class LambdaClass4{ public static void main(String[] args){ //引用 某 类 对 象 的实例方法 //方式一:Lambda表达式,类名.实例方法 (a, b, c)->a.实例方法(b, c) //Converter con = (from, i, j) -> from.substring(i, j); //方式二 Converter con = String :: substring; String val = con.convert("from Java you guess", 5, 12); System.out.println(val); } }
引用构造器 类名 :: new 函数式接口中被实现方法的全部参数传给构造器作为参数 (a, b...) -> new 类名(a, b,...)
interface YourTest{ String win(String title); } public class LambdaClass5{ public static void main(String[] args){ //方式一:Lambda表达式 //YourTest yt = (a) -> new String(a); //方式二 YourTest yt2 = String :: new; String jf = yt2.win("窗口"); System.out.println(jf); } }
注意:这里的YourTest yt = (a) -> new String(a);中String是win方法的返回值,a是要传入到win方法中的参数。所以String :: new ,实际是方法的返回值 :: new .
Lambda表达式与匿名内部类一样,都可以直接访问effectively final的局部变量,以及外部类的成员变量。
Lambda表达式创建的对象与匿名内部类生成对象一样,都可以直接调用从接口中继承的默认方法。
匿名内部类可以为任何接口创建实例,不管接口包含多少抽象方法,只要匿名内部类实现所有的抽象方法就可以。Lambda表达式只能为函数式接口创建实例。
匿名内部类可以为抽象类甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例。
匿名内部类实现的抽象方法的方法体允许直接调用接口中定义的方法,Lambda表达式的代码块不允许调用接口中定义的默认方法。
import java.util.*; public class LambdaClass7{ /* public String meMethod(interfaceClass ifc){ return "完成"; } */ public static void main(String[] args){ String waibu = "外部变量"; //LambdaClass7 lc7 = new LambdaClass7(); interfaceClass ifc = (String str) -> { String strLambda = "Lambda表达式内定义的变量"; return "外部变量: " + waibu+ "\n" + "Lambda传递的变量: " + str + "\n" + "Lambda表达式内部定义的变量: " +strLambda; }; String str = ifc.method("Lambda表达式传递的变量"); System.out.println(str); //lc7.meMethod(); } } interface interfaceClass{ String method(String str); }
Java的Lambda表达式可以访问外部变量,内部定义的变量和传递的变量,不过访问外部变量有一个限制:变量不可变,如果在表达式内部试图改变外部类的值,则会报错。以前Java的匿名内部类在访问外部变量的时候,外部变量必须用final修饰。现在java8对这个限制做了优化,可以不用显示使用final修饰,但是编译器隐式当成final来处理。(注意:这里的变量不可变是指引用地址不可变)。
在lambda中,this不是指向lambda表达式产生的那个SAM对象,而是声明它的外部对象。

浙公网安备 33010602011771号