t6.2
6.2.1 字符串的== 判断:
// s1直接引用常量池中的"疯狂Java" String s1 = "疯狂Java"; String s2 = "疯狂"; String s3 = "Java"; // s4后面的字符串值可以在编译时就确定下来 // s4直接引用常量池中的"疯狂Java" String s4 = "疯狂" + "Java"; // s5后面的字符串值可以在编译时就确定下来 // s5直接引用常量池中的"疯狂Java" String s5 = "疯" + "狂" + "Java"; // s6后面的字符串值不能在编译时就确定下来, // 不能引用常量池中的字符串 String s6 = s2 + s3; // 使用new调用构造器将会创建一个新的String对象, // s7引用堆内存中新创建的String对象 String s7 = new String("疯狂Java"); System.out.println(s1 == s4); // 输出true System.out.println(s1 == s5); // 输出true System.out.println(s1 == s6); // 输出false System.out.println(s1 == s7); // 输出false
6.2.2 两个对象相等判断:
package com.lee.test.ligang.unit6.t2; class Person { private String name; private String idStr; public Person() { } public Person(String name, String idStr) { this.name = name; this.idStr = idStr; } // 此处省略name和idStr的setter和getter方法。 // name的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } // idStr的setter和getter方法 public void setIdStr(String idStr) { this.idStr = idStr; } public String getIdStr() { return this.idStr; } // 重写equals()方法,提供自定义的相等标准 public boolean equals(Object obj) { // 如果两个对象为同一个对象 if (this == obj) return true; // 只有当obj是Person对象 if (obj != null && obj.getClass() == Person.class) { Person personObj = (Person) obj; // 并且当前对象的idStr与obj对象的idStr相等才可判断两个对象相等 if (this.getIdStr().equals(personObj.getIdStr())) { return true; } } return false; } } public class OverrideEqualsRight { public static void main(String[] args) { Person p1 = new Person("孙悟空", "12343433433"); Person p2 = new Person("孙行者", "12343433433"); Person p3 = new Person("孙悟饭", "99933433"); // p1和p2的idStr相等,所以输出true System.out.println("p1和p2是否相等?" + p1.equals(p2)); // p2和p3的idStr不相等,所以输出false System.out.println("p2和p3是否相等?" + p2.equals(p3)); } }
6.2.3 eqauls判断
int it = 65; float fl = 65.0f; // 将输出true System.out.println("65和65.0f是否相等?" + (it == fl)); char ch = 'A'; // 将输出true System.out.println("65和'A'是否相等?" + (it == ch)); String str1 = new String("hello"); String str2 = new String("hello"); // 将输出false System.out.println("str1和str2是否相等?" + (str1 == str2)); // 将输出true System.out.println("str1是否equals str2?" + (str1.equals(str2))); // 由于java.lang.String与EqualTest类没有继承关系, // 所以下面语句导致编译错误 // System.out.println("hello" == new EqualTest());
t6.3类成员
t6.4final修饰符
final成员变量
【规则】【6.4.1】final修饰的成员变量,可以申明不赋值,但必须在后面初始化快、静态代码块、构造方法中显示的赋值
【规则】宏变量的使用
import java.util.Arrays; 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; } }
String s1 = "疯狂Java"; // s2变量引用的字符串可以编译时就确定出来, // 因此s2直接引用常量池中已有的"疯狂Java"字符串 String s2 = "疯狂" + "Java"; System.out.println(s1 == s2); // 定义2个字符串直接量 String str1 = "疯狂"; //① String str2 = "Java"; //② // 将str1和str2进行连接运算 String s3 = str1 + str2; System.out.println(s1 == s3);
【规则】final方法的重写
//不可重写: public class FinalMethodTest { public final void test() { } } class Sub extends FinalMethodTest { // 下面方法定义将出现编译错误,不能重写final方法 // public void test() { // } } //可重写: public class PrivateFinalMethodTest { private final void test(){} } class Sub extends PrivateFinalMethodTest { // 下面方法定义将不会出现问题 public void test(){} }
【规则】设计不可变类
class Name { private String firstName; private String lastName; public Name() { } public Name(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } // 省略firstName、lastName的setter和getter方法 public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return this.firstName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getLastName() { return this.lastName; } } public class Person { private final Name name; // 这种构造函数会破坏final设计初衷 // public Person(Name name) { // this.name = name; // } // public Name getName() { // return name; // } public Person(Name name) { this.name = new Name(name.getFirstName(), name.getLastName()); } public Name getName() { return new Name(name.getFirstName(), name.getLastName()); } public static void main(String[] args) { Name n = new Name("悟空", "孙"); Person p = new Person(n); // Person对象的name的firstName值为"悟空" System.out.println(p.getName().getFirstName()); // 改变Person对象name的firstName值 n.setFirstName("八戒"); // Person对象的name的firstName值被改为"八戒" System.out.println(p.getName().getFirstName()); } }
【模式】缓存实例的不可变类
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); } }
【规则】Integer的缓存策略
// 生成新的Integer对象 Integer in1 = new Integer(6); // 生成新的Integer对象,并缓存该对象 Integer in2 = Integer.valueOf(6); // 直接从缓存中取出Ineger对象 Integer in3 = Integer.valueOf(6); System.out.println(in1 == in2); // 输出false System.out.println(in2 == in3); // 输出true // 由于Integer只缓存-128~127之间的值, // 因此200对应的Integer对象没有被缓存。 Integer in4 = Integer.valueOf(200); Integer in5 = Integer.valueOf(200); System.out.println(in4 == in5); //输出false
t6.7 内部类
t6.7.1 非静态内部类
package com.lee.test.ligang.unit6.t7.t1; public class Cow { private double weight; // 外部类的两个重载的构造器 public Cow() { } public Cow(double weight) { this.weight = weight; } // 定义一个非静态内部类 private class CowLeg { // 非静态内部类的两个实例变量 private double length; private String color; // 非静态内部类的两个重载的构造器 public CowLeg() { } public CowLeg(double length, String color) { this.length = length; this.color = color; } // 下面省略length、color的setter和getter方法 public void setLength(double length) { this.length = length; } public double getLength() { return this.length; } public void setColor(String color) { this.color = color; } public String getColor() { return this.color; } // 非静态内部类的实例方法 public void info() { System.out.println("当前牛腿颜色是:" + color + ", 高:" + length); // 直接访问外部类的private修饰的成员变量 System.out.println("本牛腿所在奶牛重:" + weight); // ① } } public void test() { CowLeg cl = new CowLeg(1.12, "黑白相间"); cl.info(); } public static void main(String[] args) { Cow cow = new Cow(378.9); cow.test(); } } package com.lee.test.ligang.unit6.t7.t1; public class DiscernVariable { private String prop = "外部类的实例变量"; private class InClass { private String prop = "内部类的实例变量"; public void info() { String prop = "局部变量"; // 通过 外部类类名.this.varName 访问外部类实例变量 System.out.println("外部类的实例变量值:" + DiscernVariable.this.prop); // 通过 this.varName 访问内部类实例的变量 System.out.println("内部类的实例变量值:" + this.prop); // 直接访问局部变量 System.out.println("局部变量的值:" + prop); } } public void test() { InClass in = new InClass(); in.info(); } public static void main(String[] args) { new DiscernVariable().test(); } } package com.lee.test.ligang.unit6.t7.t1; 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(); } }
t.6.7.2静态内部类
package com.lee.test.ligang.unit6.t7.t2; public class StaticInnerClassTest { private int prop1 = 5; private static int prop2 = 9; static class StaticInnerClass { // 静态内部类里可以包含静态成员 private static int age; public void accessOuterProp() { // 下面代码出现错误: // 静态内部类无法访问外部类的实例变量 // System.out.println(prop1); // 下面代码正常 System.out.println(prop2); } } } package com.lee.test.ligang.unit6.t7.t2; 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); } }
t6.7.3内部类使用规则
package com.lee.test.ligang.unit6.t7.t3; 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("测试信息"); */ } } package com.lee.test.ligang.unit6.t7.t3; class StaticOut { // 定义一个静态内部类,不使用访问控制符, // 即同一个包中其他类可访问该内部类 static class StaticIn { public StaticIn() { System.out.println("静态内部类的构造器"); } } } public class CreateStaticInnerInstance { public static void main(String[] args) { StaticOut.StaticIn in = new StaticOut.StaticIn(); /* * 上面代码可改为如下两行代码: 使用OutterClass.InnerClass的形式定义内部类变量 StaticOut.StaticIn * in; 通过new来调用内部类构造器创建静态内部类实例 in = new StaticOut.StaticIn(); */ } } package com.lee.test.ligang.unit6.t7.t3; public class SubClass extends Out.In { // 显示定义SubClass的构造器 public SubClass(Out out) { // 通过传入的Out对象显式调用In的构造器 out.super("hello"); } }
t6.8 Lambda表达式
t6.8.1 Lambda入门
package com.lee.test.ligang.unit6.t8.t1; public interface Command { // 接口里定义的process()方法用于封装“处理行为” void process(int[] target); } package com.lee.test.ligang.unit6.t8.t1; public class CommandTest { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] array = { 3, -4, 6, 4 }; // 处理数组,具体处理行为取决于匿名内部类 pa.process(array, new Command() { public void process(int[] target) { int sum = 0; for (int tmp : target) { sum += tmp; } System.out.println("数组元素的总和是:" + sum); } }); } } package com.lee.test.ligang.unit6.t8.t1; public class CommandTest2 { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] array = {3, -4, 6, 4}; // 处理数组,具体处理行为取决于匿名内部类,可以简写类型 // pa.process(array , (int[] target)->{ pa.process(array , target->{ int sum = 0; for (int tmp : target ) { sum += tmp; } System.out.println("数组元素的总和是:" + sum); }); } } package com.lee.test.ligang.unit6.t8.t1; interface Eatable { void taste(); } interface Flyable { void fly(String weather); default void fly2(String weather){ System.out.println("今天天气是:" + weather); System.out.println("直升机飞行平稳"); } } interface Addable { int add(int a, int b); } public class LambdaQs { // 调用该方法需要Eatable对象 public void eat(Eatable e) { System.out.println(e); e.taste(); } // 调用该方法需要Flyable对象 public void drive(Flyable f) { System.out.println("我正在驾驶:" + f); f.fly("【碧空如洗的晴日】"); } // 调用该方法需要Addable对象 public void test(Addable add) { System.out.println("5与3的和为:" + add.add(5, 3)); } public static void main(String[] args) { LambdaQs lq = new LambdaQs(); // Lambda表达式的代码块只有一条语句,可以省略花括号。 lq.eat(() -> System.out.println("苹果的味道不错!")); // Lambda表达式的形参列表只有一个形参,省略圆括号【形参根据接口类型自定义申明变量名】 lq.drive(weather -> { System.out.println("今天天气是:" + weather); System.out.println("直升机飞行平稳"); }); new Flyable() { @Override public void fly(String weather) { } }.fly2("【碧空如洗的晴日】");; // Lambda表达式的代码块只有一条语句,省略花括号 // 代码块中只有一条语句,即使该表达式需要返回值,也可以省略return关键字。 lq.test((a, b) -> a + b); } } package com.lee.test.ligang.unit6.t8.t1; public class ProcessArray { public void process(int[] target , Command cmd) { cmd.process(target); } }
t6.8.2 Lambda表达式与接口
java.util.function 大量函数式接口:
XxxFunction:
return apply();
XxxConsumer:
void accept();
XxxPredicate:
Boolean test();
XxxSupplier:
return getAsXxx();
package com.lee.test.ligang.unit6.t8.t2; @FunctionalInterface 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(); // } // }; 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(); } }; } }
t.6.8.3 方法引用与构造器引用
1.接口中的方法
1.1有实现
必须显式修饰static
1.2无实现