《Java核心技术》阅读笔记(一)-面向对象、内部类、Lambda基础

基本程序设计结构

每个Java应用程序都必须有一个main方法

object.method(parameters)

注释

  • 单行 //
  • 长篇 /* */
  • 自动生成文档 /** */

基本数据类型(primitive type)

强类型:必须为每个变量声明一种类型

  1. 整型
    byte
    short
    int
    long

  2. 浮点型
    float
    double
    (正无穷大、负无穷大、NaN)

  3. char(2byte)
    Unicode字符用一个或两个char类型表示。
    使用char描述所有Unicode字符:码点(17个代码级别)

  4. boolean
    整型与boolean不能转换

变量及操作

  1. 变量
    声明(命名)
    初始化

  2. 常量
    final:只能赋值一次
    类常量:在类的多个方法中使用(static final)

  3. 运算符
    加、减、乘、除(除零时的结果差异)、取模
    浮点运算,保持移植时的精确度,使用strictfp修饰类或方法(截断操作,有可能溢出)

    • Math
      计算机浮点单元中的例程,得到多平台相同结果,则使用StrictMath

    • 类型转换
      《Java核心技术》阅读笔记(一)-面向对象、内部类、Lambda基础_第1张图片
      虚箭头:有精度损失的转换
      二元运算时,先将操作数转换为同一类型,然后再进行运算:

      1. 如果其中一个操作数为double,则另一个转为double
      2. 。。。。。。。。。。float,。。。。。。。float
      3. 。。。。。。。。。。long,。。。。。。。。long
      4. 否则,两个操作数都将转为int
    • 结合赋值和运算符
      += -= *= /= (当最后变量与计算结果类型不同时,触发强制转换)
      自增自减

    • 关系和boolean运算符

    • 位运算符
      与 或 异或 非

    • 括号和运算符规则

    • 枚举

  4. 字符串
    subString
    format
    不可变:效率权衡-字符串常量共享

    CodePoint:一个或两个代码单元(char)构成

  5. 输入输出

    • 输入
      Scanner:关联标准输入System.in、scanner.nextLine、next()、netInt()
      Console:从控制台读取密码cons.readPassword(“Password:”)

    • 输出
      System.out.printf("%8.2f", x);
      System.out.printf(“Hello, %s. Next year, you’ll be %d”, name, age);
      《Java核心技术》阅读笔记(一)-面向对象、内部类、Lambda基础_第2张图片
      《Java核心技术》阅读笔记(一)-面向对象、内部类、Lambda基础_第3张图片

    • 文件的IO
      读取:Scanner in = new Scanner(Paths.get(“myfile.txt”), “UTF-8”);
      写入:PrintWriter out = new PrintWriter('myfile.txt", “UTF-8”);
      确定启动路径:String dir = System.getProperty('user.dir"):

  6. 控制流程

    • 块作用域

    • 条件语句

    • 循环
      while:为真才执行
      dowhile: 先执行再判断,至少执行一次
      for

    • 多重选择switch语句
      case标签可以为:byte、short、int、char、枚举变量、字符串字面量,注意case贯穿问题

    • 中断控制流程

      • continue

      • 带标签的break——跳出多层嵌套的控制

        label:
        {
            // deal with bad situation
            // carry out normal processing
            if (condition) break label; // exits block
            ... 
        }
        
        // jumps here when the break statement executes
        

对象与类

类之间关系

  1. 依赖
  2. 关联(知道、了解)
  3. 聚合(拥有)
  4. 继承

static

  1. 静态域
    也称类域,属于类,不属于任何类的对象。而实例域,每一个对象都有其拷贝。
    初始化:声明时指定初始化值、静态初始化块初始化

    // static initialization block
    static
    {
        Random generator = new Random();
        nextld = generator.nextlnt(1OOOO);
    }
    
  2. 静态final域(静态常量)
    示例:Math.PI、System.out(setOut native方法)

  3. 静态方法

    特点:

    • 没有隐式参数
    • 可以访问静态域
    • 可通过对象调用(易混淆)

    使用场景:

    • 只通过显示参数完成执行的方法(不需访问对象状态的方法)
    • 只需访问类静态域的方法
    • factoryMethod构造对象
      解决构造器问题:无法命名、改变构造类型(返回对象为子类对象)
    • main方法(可用于单元测试)

对象

对象的三个特性:行为、状态、标识

域(对象域或实例域)

声明

  1. private(pulic破坏封装性)
  2. 可选final修饰(大都应用于基本类型primitive、不可变类Immutable)

封装的好处(提供域访问器、域更改器方法mutator method、accessor method)

  1. 可以改变内部实现,只影响该类的代码
  2. 更改器方法可以执行错误检查

访问器

  1. 不要编写返回引用可变对象的访问器方法。若要返回,需先clone
  2. 类中的每个方法都不会改变其对象,这种类就是不可变类
  3. C++注释:const修饰的表示访问器方法

初始化

  1. 构造器中设置值(创建类时初始化默认值–不同于局部变量)
  2. 生命中赋值
  3. 初始化块(initialization block,构造器执行前执行)

方法

  • 对内:访问域
    类方法可以访问所属类任何对象的私有域

    public boolean equals(Employee other)
    {
        return name.equals(other.name);
    }
    
  • 对外:方法调用

    • 方法signature:方法名、参数类型;返回类型不属于方法签名
    • 重载:方法名称相同、参数不同
    • 重载解析(编译阶段)

参数

  • 类型:

    • 隐式(implicit)、显式(explicit)
      (this:调用对象域、构造方法)
    • 基本数据类型、对象引用
  • 传值:无论是原始类型、还是引用类型变量,都会复制值

    • 方法无法修改基本数据类型参数
    • 可改变对象参数的状态
    • 不能让对象参数引用新对象

import 类、静态域、静态方法

继承(Inheritance)

多态的含义
枚举类的使用
反射是什么?

Object基础方法

equals

规则:

  1. 自反性
  2. 对称性
  3. 传递性
  4. 一致性
  5. 对于任意非空引用,x.equals(null)返回false

实现:

  1. 参数命名为otherObject,转为other变量
  2. 检测this与otherObject是否引用同一对象
  3. otherObject是否为null
  4. this与otherObject是否属于同一类
    • 子类拥有自己的相等概念,用getClass
    • 由超类决定相等概念,用instanceOf
  5. 转换otherObject为相应类类型变量
  6. 对需要域进行比较,对象域用
    • Objects.equals比较,基本类型域用 ==
    • 若子类中重新定义equals,就要在其中包含super.equals(other)

hashCode

实现:

  1. Objects.hashCode() ——参数为null时返回0
  2. Double.hashCode() ——避免创建Double对象
  3. Objects.hash(……) ——对参数调用hashCode方法,并组合

toString

  1. getClass().getName + “[attrName:value,……]”
  2. Arrays.toString 、Arrays.deepToString

反射

能够分析类能力的程序称为反射(reflective)

作用:

  1. 运行时分析类的能力
  2. 运行时查看对象
  3. 实现通用的操作(示例:通用数组扩容)

Class

保存运行时的类型信息

  1. 获取Class对象

    • Object.getClass()
    • Class.forName() --可用于程序启动时提供加载页面
    • Type.class --示例:Class cl = int.class;

    每个类型一个类对象:e.getClass() == Employee.class

  2. 创建类对象

    • clzz.newInstance() 调用默认的构造方法,若没有默认构造器则抛出异常
    • constructor.newInstance()

分析类

获取域、方法:

  1. getFields、getMethods、getConstructors 获取类及其超类的public成员
  2. getDeclareFields、getDeclareMethods、getDeclareConstructors 获取全部成员(不包括超类成员)

Field

  1. 获取值
    f.get(obj) --安全管理器控制,私有域访问需覆盖访问控制
    f.getDouble(obj)

  2. 设置值
    f.set(obj, value)
    AccessibleObject.setAccessible(true)

Method

clazz.getMethod(name, Class… parameterTypes)
m.invoke(obj, Object…) --没有隐式参数,obj传null

Constructor

公共方法:

  1. getName
  2. getModifiers(可通过Modifie静态方法分析、toString方法可打印修饰符)
  3. setAccessible(true)

泛型数组扩容(应用)

Arrays.copyOf

问题:new Object[newLength] ——不能直接扩展数组,需要创建与原类型相同的新数组
解决:Array.newInstance()

Class.getComponentType()
Array.getLength(a)
System.arraycopy()

继承设计技巧

  1. 公共操作和类放到超类
  2. 不要使用受保护的域(不要设置域为protectd–子类集合无限制破坏封装性,同一个包的类也可访问)
  3. 使用继承实现is-a关系
  4. 除非所有继承的方法都有意义,否则不要使用继承
  5. 覆盖方法时,不改变预期行为
  6. 使用多态,而非类型信息
  7. 不要过多使用反射。

接口

概念

接口不是类,是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义

类实现接口:实现方法指定为public,否则为包可见性

注:继承情况下的反对称规则
违反时抛出ClassCastException:

  1. 如果子类之间的比较含义不一样,那就属于不同类对象的非法比较。每个compareTo方法都应该在开始时进行下列检测:
    if(getClass()!=other.getClass())thrownewClassCastException()
  2. 如果存在这样一种通用算法,它能够对两个不同的子类对象进行比较,则应该在超类中提供一个compareTo方法,并将这个方法声明为final

特性

  • 不可实例化,但可定义接口类型变量,可被instanceOf检测
  • 可被扩展
  • 可包含常量,默认push static final
  • 一个类可实现多个接口
  • 默认方法为接口方法提供默认实现,可调用其他方法(用法:接口演化)

注:默认方法的冲突解决?

  1. 超类优先(与JavaSE7兼容)
  2. 接口冲突需要覆盖

对比抽象类

可将接口看成没有实例域的抽象类

存在了抽象类,还需要接口:每个类只能扩展于一个类。
接口可以提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效性。

接口示例

  1. callback机制:指出某个特定事件发生时应该采取的动作

  2. Comparator
    Arrays.sort

  3. Cloneable

    如果希望copy是一个新对象,它的初始状态与original相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用clone方法

    默认的clone操作是”浅拷贝“,克隆对象与原对象共享子对象为可变对象时,调用更改器方法会篡改另一对象的状态,造成不一致现象。

    1. 默认的clone方法是否满足要求;
    2. 是否可以在可变的子对象上调用 clone 来修补默认的clone方法;
    3. 是否不该使用clone(默认)

    Cloneable接口只是作为一个标记,指示类设计者了解克隆过程

标记接口不包含任何方法;它唯一的作用就是允许在类型查询中使用 instanceof

Lambda表达式

概念

是一个可传递的代码块,可以在以后执行一次或多次。

之前:在Java中传递一个代码段并不容易,不能直接传递代码段。Java是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码。

语法

(参数) -> 表达式

  • 参数类型如果可推导,则可忽略
  • 参数只有一个,且类型可推导,小括号可忽略
  • 无需返回类型,根据上下文推导得出

应用

对于只有一个抽象方法的接口,当需要这个接口的对象时,就可以提供一个lambda表达式,这种接口被称为函数式接口

方法引用

可能已经有现成的方法可以完成你想要传递到其他代码的某个动作

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod (第一个参数称为方法目标)
    可以使用this、super参数
构造器引用

方法名为new的方法引用

可以用数组类型建立构造器引用。如int[]::new

Java有一限制:无法构造泛型类型 T 的数组,可利用数组构造器解决:
Person[] people = stream.toArray(Person[]::new);

变量作用域

lambda表达式有3部分:

  1. 方法体
  2. 参数
  3. 自由变量的值
    指非参数而且不在代码中定义的变量(被lambda捕获)
    实现:lambda被转换为包含一个方法的对象,自由变量的值复制到实例变量中。
    (闭包(closure):一种引用了外部作用域变量的特殊函数)

规则:lambda表达式中捕获的变量必须实际上是最终变量
(effectively final:这个变量初始化之后就不会再为它赋新值)
在lambda中改变值,并发情况不安全。在外部改变也不允许。

this关键字,是指创建这个lambda表达式方法的this参数

处理lambda

使用lambda是为了:延迟执行(deferred execution)

  • 在一个单独的线程中运行代码;
  • 多次运行代码;
  • 在算法的适当位置运行代码(例如,排序中的比较操作;)
  • 发生某种情况时执行代码(如,点击了一个按钮,数据到达等等;)
  • 只在必要时才运行代码。

选择或提供一个函数式接口,常见如下表:
《Java核心技术》阅读笔记(一)-面向对象、内部类、Lambda基础_第4张图片
《Java核心技术》阅读笔记(一)-面向对象、内部类、Lambda基础_第5张图片

内部类

inner class:定义在一个类中的类
For:

  • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据;
  • 内部类可以对同一个包中的其他类隐藏起来;
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。

使用

  • 内部类的对象总有一个隐式引用, 它指向了创建它的外部类对象
  • 内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域
  • 只有内部类可以声明为私有的,这样只有外围类能构造内部类对象。

特殊语法规则
引用外围类表达式:OuterClass.this.beep
内部类构造器outerObject.new InnerClass(construction parameters)
外围类作用域之外引用类:OuterClass.InnerClass

内部类中声明的所有静态域都必须是 final
内部类不能有static方法。Java语言规范对这个限制没有做任何解释。也可以允许有静态方法,但只能访问外围类的静态域和方法

原理

内部类是一种编译器现象,虚拟机对此一无所知。编译器为内部类生成了带有外部类参数的构造方法,并提供了访问外部类对象状态的静态方法

反射反编译解析字节码
java reflection.ReflectionTest irmerClass.lil kingClock\STimePrinter

javap -private ClassName

可以创建同一个包下的类利用虚拟机指定获取内部类访问的私有变量,破坏安全性。

局部内部类

在方法中定义的局部类,不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中
优势:

  1. 对外部世界可以完全地隐藏起来
  2. 不仅能够访问包含它们的外部类, 还可以访问局部变量。不过,那些局部变量必须事实上为 final(有时final限制不方便,可以使用一个元素的数组代替参数)

匿名内部类

如果只创建局部内部类的一个对象,就不必命名了,这种类被称为匿名内部类(anonymous innerclass)

new SuperType(construction parameters) {
inner class methods and data
}

匿名类不能有构造器,取而代之的是,将构造器参数传递给超类。
实现接口时不能有任何构造参数。

应用技巧:

  • “双括号初始化invite” (new ArrayList() {{ add(“Harry”); add(“Tony”); }});
  • 静态方法生成类名信息 new Object(){}.getCIass().getEndosingClass0 // gets class of static method

注意:实现equals方法时,对于匿名子类getClass测试会失败;

静态内部类

当使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为 static, 以便取消产生的引用。

  • 与常规内部类不同,静态内部类可以有静态域和方法
  • 声明在接口中的内部类自动成为 static 和 public 类
    只有内部类可以声明为static

代理

For:利用代理可以在运行时创建一个实现了一组给定接口的新类,用于:

  • 路由对远程服务器的方法调用
  • 在程序运行期间,将用户接口事件与动作关联起来
  • 为调试, 跟踪方法调用

创建的类具有下列方法:

  • 指定接口所需要的全部方法;
  • Object类中的全部方法。

提供一个调用处理器( invocation handler)

使用

创建代理对象:Proxy的newProxyInstance方法,参数包含

  • class loader,null表示默认的类加载器;
  • 一个class对象数组,元素要实现的接口;
  • 一个调用处理器

特性

  • 代理类在程序运行中创建,所有代理类扩展于Proxy类,一个代理类只有一个实例域——调用处理器
  • 所有的代理类都覆盖了 Object 类中的方法 toString、 equals 和 hashCode
  • 对于特定的类加载器和预设的一组接口来说,只能有一个代理类(可用getProxyClass方法获得这个类)
  • 代理类一定是 public 和 final。
  • Proxy.isProxyClass检测特定Class是否是代理类

小结

到此为止,Java 程序设计语言的基础概念整理完毕。包括Java编程中面向对象相关基础、接口相关知识,以及内部类、Java8新增的Lambda表达式等。

你可能感兴趣的