core-java 阅读笔记(java基础)

java基本程序设计环境

  • java数据类型:强类型,每个变量必需声明一种类型

    • 整型:int(4字节)     short(2字节)     long(8字节)     byte(2字节),整型范围与平台无关,java没有unsigned整型类型

    • 浮点类型:float(4字节)     double(8字节),三个特殊浮点数值:正无穷大(Double.POSITIVE_INFINITY),负无穷(Double.BEGATIVE_INFINITY),NAN(Double.NAN);浮点数的误差:二进制无法精确表示某些分数(如1/10)

    • char类型:使用Unicode字符集,范围:\u0000-\uffff

    • boolean类型

  • java变量

    • 变量声明:每个变量有一个类型,位于变量名之前

    • 变量名:以字母开头,由字母和数字组成(字母包括 ’ A ’ ~ ’ Z ’ 、 ’ a 1 ~ ’ z 、或在某种语言中表示字母的任何 Unicode 字符),变量名大小写敏感,不可使用java保留字作为变量名

      • 变量初始化:使用赋值语句显示初始化,不可使用未初始化的变量(变量的声明尽可能地靠近变量第一次使用的地方)

      • 常量:关键字final指示常量,表示该变量只能赋值一次,习惯上常量使用全大写字母表示;staic final指示类常量,可在一个类的多个方法调用

  • 运算

    • 整数被0除会产生异常,浮点数被0除会得到无穷大或NAN;

    • strictfp关键字:使用严格的浮点计算,每次计算的中间值存储在 80 位的寄存器中 , 最终结果截断为 64 位

    • 数值类型转换:运算时将操作数转换为相同数据类型(向精度高的转换)

    • 强制类型转换:可能导致数据丢失

      double x = 9.997; int nx = (int)x;//double类型强制转换为int类型

    • 其他运算:赋值运算(+= -= *=...)自增自减(++ --)关系逻辑(&& || > ==...),逻辑运算具有短路运算特点,位运算(^ & | ~ >> << <<<)

  • 字符串

    • java字符串是Unicode字符序列,java没有内置的字符串类型,标准类库中有String类

    • 子串:String类的substring方法可从长字符串中提取字串

    • 拼接:可使用+连接两个字符串,将多个字符串连接可使用String.join方法

    • String对象不可修改(即不可变字符串),字符串变量的修改实质上是引用另一个字符串。不可变字符串的优点:实现字符串的共享

    • 检测字符串相等:equals方法,==比较的是字符串是否是同一个对象,而非字符串内容是否相等

    • 空串与null:空串""是长度为0的字符串,null是未指向对象的字符串变量

    • 构建字符串:StringBuilder类

  • 输入输出

    • 标准输出流:System.out;标准输入流:System.in,使用标准输入流需要构造Scannner对象,并与System.in关联

      Scanner scan = new Scanner(System.in);//使用标准输入流

    • 格式化输出:System.out.printf,每一个以 % 字符开始的格式说明符都用相应的参数替换 ;使用静态的 String.format 方法创建一个格式化的字符串, 不打印输出

    • 文件输入与输出

      • 文件读取:使用File对象构造Scanner对象(如果使用文件名字符串作为参数传入,会被Scanner对象解释为数据)

        Scanner in = new Scanner(Paths.get("niyflle.txt"),"UTF-8");

      • 文件写入:构造PrintWriter对象,只须提供文件名(如果文件不存在,则自动新建文件)

        PrintWriter out = new PrintlulriterC ' myfile txt" , "UTF - 8" ) ;

  • 控制流程

    • 块作用域

    • if else语句

    • while循环

    • do while循环

    • for循环(在循环中判断浮点数相等可能会出现死循环)

    • switch语句

 

对象与类

  • 面向对象程序设计概述

    • 类:构造对象的模板

    • 对象:三特性(行为、状态、标识)

    • 类的设计:名词——》成员域     动词——》方法

    • 类之间的关系:依赖(use-a:一个类的方法操纵另一个类的对象)、聚合(has-a:一个类的对象包含另一个类的对象)、继承(is-a)

  • 用户自定义类

    • 数据的封装:私有成员域+setter方法+getter方法(不要编写返回引用对象的setter方法,因为返回的是引用值,会导致私有成员域在外部被修改,破坏封装性;如果需要返回对象的引用,应该先对其进行克隆,将其保存为另一个对象副本)

  • 静态域与静态方法

    • 静态域(类域):每个类中只有一个;实例域:每个对象都有一个拷贝

    • 静态常量:static final

    • 静态方法:不能向对象实施操作的方法,不可访问实例域,通过类名调用(也可通过对象调用)。

  • 方法参数

    • java参数传递:值传递

      • 基本数据类型:不可被修改

      • 引用数据类型(值为地址):可被修改

      • 一个方法不能修改一个基本数据类型的参数 (即数值型或布尔型 ) 。

      • 一个方法可以改变一个对象参数的状态 。

      • 一个方法不能让对象参数引用一个新的对象 。

  • 对象构造

    • 重载:多个相同名字但参数不同的方法

    • 默认域初始化:

    • 如果在构造器中没有显式地给域赋予初值 , 那么就会被自动地赋为默认值 : 数值为 0 、布尔值为 false 、 对象引用为 null 。

    • 无参构造器:只有类没有编写构造器时,系统才会提供一个无参构造器

    • 构造器中调用另一个构造器:this

    • 初始化块:先于构造器被初始化

    • 使用包可以确保类名唯一

    • 一个类可以使用所属包内的所有类以及其他包的公共类

    • 使用import导入类和静态方法和静态域

    • 要将一个类放人包中 , 就必须将包的名字放在源文件的开头

    • 没有指定 public 或 private 的部分 ( 类 、 方法或变量 )可以被同一个包中的所有方法访问 。

  • 类路径

    • 类的路径必须与包名匹配 。

    • 为了使类能够被多个程序共享, 需要做到下面几点 :

      • 把类放到一个目录中, 例如 / home / user / classdir 。 需要注意, 这个目录是包树状结构的基目录 。 如果希望将 com . horstmann . corejava . Employee 类添加到其中, 这个 Employee . class类文件就必须位于子目录 / home / user / classdir / com / horstmann/corejava 中 。

      • 将 JAR 文件放在一个目录中 , 例如 : / home / user / archives 。

      • 设置类路径 ( classpath ) 。 类路径是所有包含类文件的路径的集合 。

  • 文档注释

    • 在源代码中添加以专用的定界符 /** 开始的注释,可以使用javadoc生成注释文档

    • 需要编写注释的部分(注释:

      • 公有类与接口

      • 公有的和受保护的构造器及方法

      • 公有的和受保护的域

    • 类注释:放在import语句之后,类定义之前

    • 方法注释:放在方法之前,可用以下标记

      • @para变量描述

      • @return描述

      • @throws描述

    • 域注释:只需要对公有域注释

    • 通用注释:

      • @author姓名

      • @version:产生版本描述

      • @since:引入特性的版本描述

  • 类设计技巧

    • 保证数据私有

    • 要对数据初始化

    • 不要在类中使用过多基本数据类型

    • 不是所有域都需要getter和setter

    • 将方法过多的类进行分解

    • 类名和方法名要体现功能

    • 优先使用不可变的类

 

 

继承

  • 类、超类和子类

    • 定义子类:使用关键字extends从一个类继承(java中所有继承都是公有继承)

      • extends表示正在构造的新类派生于已存在的类,已存在的类称为超类、基类或父类,新类称为子类、派生类

      • 子类自动继承超类的成员域和方法,超类不可使用子类的域和方法,子类可使用超类的域和方法

    • 覆盖方法(重写)

      • 对于超类不适用的方法,子类可以提供一个新方法来覆盖超类中的这个方法

      • 子类的方法不能直接访问超类的私有域(虽然每个子类对象都拥有这些成员域),访问私有域需要通过公有的接口

      • 调用超类的方法,需要使用关键字super

    • 子类构造器:

      • 因为子类不能直接访问超类的私有域,所以可以调用超类的构造器来对该部分进行初始化,调用方法:super()

      • 如果子类的构造器没有显式地调用超类的构造器 , 则将自动地调用超类默认 ( 没有参数 )的构造器 。 如果超类没有不带参数的构造器, 并且在子类的构造器中又没有显式地调用超类的其他构造器 则 Java 编译器将报告错误 。

      • 子类类型的变量既可以引用子类对象也可引用超类对象(多态)

    • 多态

      • 对象变量是多态的,子类类型的变量可以引用子类对象也可以引用超类对象

      • 不能把超类类型的引用赋给子类变量

    • 阻止继承:final类和方法

      • 不能被扩展的类称为final类,使用final修饰符声明

      • 被声明为final的方法不能被覆盖

      • final类中的所有方法自动成为final方法

    • 强制类型转换

      • 将一个子类的引用赋给一个超类变量 , 编译器是允许的 。 但将一个超类的引用赋给一个子类变量, 必须进行类型转换。

      • 只能在继承层次内进行类型转换 。

      • 在将超类转换成子类之前 , 应该使用 instanceof 进行检查 。

    • 抽象类

      • 使用abstract关键字声明的方法是抽象的,不需要实现

      • 包含一个或多个抽象方法的类必须被声明为抽象类

      • 抽象类也可以有具体的数据域和方法

      • 抽象方法的具体实现在子类中

      • 如果扩展抽象类没有实现所有抽象类方法,那么子类也必须定义为抽象的

      • 不含抽象方法的类也可以被定义为抽象类

      • 抽象类不能被实例化,可以定义抽象类的变量,但只能引用非抽象类型的子类实例

    • 受保护访问

      • private私有欲对其他类是不可见的,子类也不能访问超类的私有域

      • 如果子类希望访问超类的某些域或方法,可以将域声明为protected

  • Object:所有类的超类

    • 可以使用Object类型的变量引用任何类型的对象

    • equals方法:检测两个对象是否相等,即两个对象是否具有相同的引用

    • 相等测试与继承,equals方法的要求

      • 自反性:对任意非空引用x,x.equals(x)返回true

      • 对称性:对任意非空引用x、y,当且仅当 y.equals(x)返回true, x.equals(y)也应该返回true。

      • 传递性:对于任何引用x、y和z, 如果x.equals(y)返回true, y.equals(z)返回true,x.equals(z)也应该返回true。

      • 一致性: 如果x和y引用的对象没有发生变化, 反复调用x.equals(y) 应该返回同样的结果。

      • 对于任意非空引用 x, x.equals(null) 应该返回 false

    • hashCode方法:获得对象的散列值

    • toString方法:返回表示对象值的字符串,格式(类名[域值])

  • 泛型数组列表

    • java允许运行时确定数组的大小

    • ArrayList泛型类:可自动调节数组大小(数组列表元素操作:add()添加,set()修改, get()获取,remove()删除)

  • 对象包装器与自动装箱

    • java所有基本类型都有一个与之对应的类,称为包装器

    • 对象包装器是不可变的,即一旦构造了包装器就不允许更改包装在其中的值;包装器类是final的,不能定义他们的子类

    • 泛型中的类型参数不允许是基本数据类型,需要用到基本类型的包装器

    • 基本类型自动转换为包装器类型的特性称为自动装箱

    • 将包装器类型赋值给基本类型时,会自动拆箱

    • 包装器类引用可以为nll,可能产生NullPointerException

  • 参数数量可变的方法

    • java提供了可以用可变的参数数量调用的方法,如printf()方法

      System.out.printf("%d",n);

       

      System.out.printf("%d,%s",n,"weights")

    • 实际上,java通过使用参数数组作为参数来实现可变参数数量的方法,格式Object...values

  • 枚举类

    • 枚举类型是一个实例数量确定的类

    • 所有枚举类型继承自Enum类,toSring()方法可以返回枚举常量名,静态values()方法可以返回一个包含全部枚举常量的数组

  • 反射

    • 什么是反射?反射机制是能够分析类能力的程序

    • 反射机制能干什么?

      • 在运行时分析类的能力

      • 在运行时查看对象

      • 实现通用的数组操作代码

      • 利用Method对象

    • Class类

      • 程序运行时,系统会为所有对象维护一个被称为运行时的类型标识,该信息跟踪这每个对象所属的类

      • Class类保存着这些信息,可以通过其访问这些信息。获得Class对象的三种方法

        • Object类中的getClass()方法会返回一个Class类型实例,Class类的getName()方法会返回类的名字(如果类在一个包里,包的名字也作为类名的一部分),

        • 还可以通过静态方法forName()获得类名对应的Class对象

        • 如果 T 是任意的 Java 类型 ( 或 void 关键字) ,T.class 将代表匹配的类对象 。

      • String s;

         

        Class c1 = s.getClass; //返回一个Class类型实例

         

        System.out.print(c.getName()); //输出s的类名String

         

        Class c2 = Class.forName("java.util.Random");//获得java.util.Random对应的Class对象

        Class c3 = Random.class;

      • class对象实际上表示的是一个类型,这个类型可能不是一种类

      • 虚拟机为每种类型管理一个Class对象

    • 利用反射分析类的能力:java.lang.reflect中的类

      • Field:描述类的域:getName()返回项目名;getType()返回描述域所属类型的 Class 对象;

      • Method:描述类的方法

      • Constructor:描述类的构造器

      • Modifier:描述类的域、方法、构造器的修饰符

      • Class的getFields、getMethods、getConstructors方法分别返回类提供的public域、方法和构造器数组,包括超类的公有成员;

      • Class 类的 getDeclareFields 、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域 、 方法和构造器, 其中包括私有和受保护成员, 但不包括超类的成员 。

    • 在运行时使用反射分析对象

      • 利用反射机制可以查看在编译时还不清楚的对象域

      • Field的get(obj)返回一个对象,其值为obj域的当前值,set(obi,value)修改修改obj对象域的值

      • 反射机制的默认行为受限于java的访问控制,如果一个 Java 程序没有受到安全管理器的控制 , 就可以调用 Field 、 Method 或Constructor 对象的 setAccessible 方法

        覆盖访问控制 。

    • 使用反射编写泛型数组代码

    • 调用任意方法

    • Method类的invoke方法

  • 继承的设计技巧

    • 将公共操作和域放在超类

    • 不要使用受保护的域

    • 使用继承实现"is-a"

    • 除非所有继承的方法都有意义, 否则不要使用继承

    • 在覆盖方法时, 不要改变预期的行为

    • 使用多态 , 而非类型信息

    • 不要过多地使用反射

 

 

接口、lambda表达式与内部类

  • 接口

    • 接口概念

      • 接口不是类,是对类的一组需求描述

      • 接口中的所有方法自动属于public

      • 为了让类实现一个接口, 通常需要下面两个步骤 :1 ) 将类声明为实现给定的接口 。2 ) 对接口中的所有方法进行定义 。

      • 要将类声明为实现某个接口 , 需要使用关键字 implements

    • 接口特性

      • 接口不是类,不能使用new运算符实例化一个接口

      • 可以声明接口的变量,接口变量必须引用实现了接口的类对象

      • 可以使用instancof检查一个对象是否实现了接口

      • 接口可以被扩展

      • 接口不能包含实例域或静态方法,但可以包含常量,即其中的域被自动设为public static final

      • 每个类只能有一个超类,但可以实现多个接口

    • 静态方法

      • Java SE 8允许在接口中添加静态方法,虽然合法但违背了接口作为抽象规范的初衷

      • 目前通常的做法都是将静态方法放在伴随类中

    • 默认方法

      • 可以为接口提供默认实现,使用default修饰符标记

      • 对于只关心接口中的部分方法时,可以将接口所有方法声明为默认方法,只实现自己需要的接口方法。

    • 解决默认方法冲突

      • 如果先在一个接口中将一个方法定义为默认方法 , 然后又在超类或另一个接口中定义了同样的方法,那么

        • 超类优先

        • 接口冲突:必需覆盖冲突的方法

    • 接口示例:接口与回调,Comparator接口,对象克隆

  • lambda表达式

    • 为什么引入lambda表达式

      • lambda表达式是一个可传递的代码块

      • 需要将一个代码块传递给某个对象,该代码块在将来某段时间被调用

      • java不能直接传递代码段,必须构造一个对象 , 这个对象的类需要有一个方法能包含所需的代码

    • lambda表达式的语法

      • lambda表达式是一个代码块,以及必须传入代码的变量规范

      • lambda表达式的一种形式:(参数)->表达式(如果如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中,并包含显式的return 语句 。

      • 即使lambda表达式没有参数也要提供空括号

      • 如果可以推导出一个 lambda 表达式的参数类型, 则可以忽略其类型

        Comparator<String> comp

        = ( first , second ) // Same as ( String first , String second )

        -> first.length() - second.length() ;

      • 如果方法只有一个参数 , 而且这个参数的类型可以推导得出 , 那么甚至还可以省略小括号

        ActionListener listener = event - >

        System.out.println ( " The time is " + new Date() " ) ;

        // Instead of (event) - > . . or ( ActionEvent event) ->...

      • 无需指定 lambda 表达式的返回类型 。 lambda 表达式的返回类型总是会由上下文推导得出

    • 函数式接口

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

      • 方法引用:方法引用等价与lambda表达式,使用::分割方法名和类名或对象名

        Timer t=new Timer (1000 , System.out::println );//将println方法传递给构造器,System,out.println等价于event->System.out.println(event)

      • 可以在方法引用中使用this和super

    • 构造器引用:方法名为new

    • 变量作用域:

      • lambda 表达式可以捕获外围作用域中变量的值,要确保所捕获的值是明确定义的

      • 在 lambda 表达式中, 只能引用值不会改变的变量

      • 如果在 lambda 表达式中引用变量, 而这个变量可能在外部改变 , 这也是不合法的

      • lambda 表达式中捕获的变量必须实际上是最终变量 ( effectivelyfinal 。实际上的最终变量是指, 这个变量初始化之后就不会再为它赋新值 。

      • lambda 表达式的体与嵌套块有相同的作用域,在 lambda 表达式中声明与一个局部变量同名的参数或局部变量是不合法的。

      • 在一个 lambda 表达式中使用 this 关键字时, 是指创建这个 lambda 表达式的方法的 this参数 。

    • 处理lambda表达式

      • 使用lambda表达式的重点是延迟执行,如以下情况:

        • 在一个单独的线程中运行代码 ;

        • 多次运行代码 ;

        • 在算法的适当位置运行代码 ( 例如 , 排序中的比较操作 ;)

        • 发生某种情况时执行代码 ( 如, 点击了一个按钮 , 数据到达, 等等 ;

        • 只在必要时才运行代码 。

      • 要接受 lambda 表达式 , 需要选择 (偶尔可能需要提供) 一个函数式接口

  • 内部类

    • 内部类是定义在另一个类中的类,使用内部类的原因有:

      • 内部类方法可以访问该类定义所在作用域的数据,包括私有数据

      • 内部类可以对同一个包中的其他类隐藏起来

      • 使用匿名内部类可以更便捷地定义一个回调函数

    • 使用内部类访问对象状态

      • 内部类既可以访问自身的数据域, 也可以访问创建它的外围类对象的数据域 .

      • 内部类的对象总有一个隐式引用, 它指向了创建它的外部类对象 。

      • 只有内部类可以定义为私有

    • 局部内部类

      • 局部类不能用 public 或 private 访问说明符进行声明 。 它的作用域被限定在声明这个局部类的块中 。

      • 局部类对外部世界可以完全地隐藏起来

    • 匿名内部类

      • 只创建一个类的对象,不需要命名

      • 匿名内部类没有类名,不能有构造器,将构造器参数传给超类构造器

    • 静态内部类:只是为了把一个类隐藏在另外一个类的内部 , 并不需要内部类引用外围类对象 。

  • 代理

    • 代理的功能:在运行时创建实现给定接口的新类

    • 使用时机:需要构造在编译时无法知道确切类型的表示接口的Class对象

    • 代理类能在运行时创建实现指定接口的全新的类,具有指定接口的全部方法以及Object类的全部方法

    • 创建代理类对象:使用Proxy类的newProxyInstance方法

 

 

异常、断言和日志

 

  • 处理错误

    • 如果由于出现错误而使得某些操作没有完成, 程序应该 :

      • 返回到一种安全状态, 并能够让用户执行一些其他的命令 ; 或者

      • 允许用户保存所有操作的结果, 并以妥善的方式终止程序

    • 异常分类

      • 异常对象都是派生与Throwable的一个实例

      • 异常分为Error和Exception

        • Error:Java 运行时系统的内部错误和资源耗尽错误,不能抛出

        • Exception:分为RuntimeException和其他异常。由程序错误导致的异常属于 RuntimeException;而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常

        • 派 生 于 Error 类 或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常 , 所有其他的异常称为受查 ( checked ) 异常

    • 声明受查异常

      • 方法应该在其首部声明所有可能抛出的异常

      • 需要抛出异常的情况:

        • 调用一个抛出受检异常的方法

        • 程序运行过程中发现错误

        • 程序出现错误

        • java虚拟机和运行时库出现的内部错误

      • 如果一个方法有可能抛出多个受查异常类型 , 那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开 。

      • 子类方法中声明的受查异常范围必须低于超类,如果超类方法没有抛出任何受查异常, 子类也不能抛出任何受查异常 。

    • 如何抛出异常

      • 抛出异常的操作流程

        • 找到一个合适的异常类

        • 创建这个类的一个对象

        • 将对象抛出

      • 一旦方法抛出了异常,这个方法就不能返回到调用者

    • 创建异常类:只需要定义一个派生于Exception的类,或者派生于Exception的子类

  • 捕获异常

    • 捕获异常

      • 如果异常发生时没有进行捕获,程序就会终止执行并在控制台输出异常信息

      • 要捕获一个异常,需要设置try/catch语句

        try{

         

        code

         

        }

         

        catch(ExceptionType e){

         

        code

         

        }
      • 如果在try语句块出现了catch子句中说明的异常类,那么程序会跳过try语句块的其余代码,执行catch语句中的处理器代码

      • 如果try语句中没有出现异常,catch语句会被跳过

      • 如果方法中的任何代码出现了在catch语句中没有声明的异常,那么该方法会立即退出

      • 如果超类没有声明抛出异常,那么对于所有在子类中可能出现的异常都必须进行捕获

    • 捕获多个异常

      • 一个try语句可以捕获多个异常类型,并对不同类型的异常进行捕获,可以为每个异常类型使用一个单独的catch语句

      • 使用异常对象的getMessage方法可以获得与异常对象本身有关的信息

    • 再次抛出异常与异常链

      • 在catch子句中可以再次抛出异常,目的是改变异常的类型

    • finally子句

      • 不管是否有异常被捕获, finally 子句中的代码都被执行

      • 可以在finally子句中关闭资源或执行未处理的代码

    • 使用异常机制的技巧

      • 异常处理不能代替简单的测试:捕获异常花费的时间多

      • 不要过分地细化异常:会使代码了膨胀

      • 利用异常层次结构

      • 不要压制异常

  • 使用断言

    • 断言的概念

      • 断言机制:允许在测试期间向代码插入检查语句

      • 关键字assert,两种形式:assert 条件         assert 条件:表达式

      • assert会对条件进行检测,如果结果为false,则抛出一个 AssertionError 异常。表达式将被传人 AssertionError 的构造器, 并转换成一个消息字符串。

    • 启用和禁用断言

      • 在默认情况下, 断言被禁用。 可以在运行程序时用 - enableassertions 或 - ea 选项启用

      • 选项 disableassertions 或 - da 禁用某个特定类和包的断言

      • 系统类需要使用 - enablesystemassertions / esa 开关启用断言

    • 使用断言完成参数检查

      • 断言失败是致命的 、 不可恢复的错误 。

      • 断言检查只用于开发和测阶段

  • 记录日志

  • 调试技巧

 

泛型程序设计

  •  为什么使用泛型程序设计

    • 类型参数的优点

      • 避免了强制类型转换

      • 使程序具有更好的可读性和安全性

  • 简单定义泛型类

    • 泛型类就是具有一个或多个类型变量的类

    • 泛型类引入类型变量,用<>括起,泛型类可以有多个类型变量

    • 用具体的类型替换类型变量就可以实例化泛型类型

  • 泛型方法

    • 泛型方法是带有类型参数的方法,可以定义在普通类和泛型类中

    • 调用一个泛型方法时 在方法名前的尖括号中放人具体的类型

  • 类型变量的限定

    • 可以将类型参数限定为实现了某些接口的类<T extends 接口>

  • 泛型代码与虚拟机

    • 虚拟机没有泛型对象

    • 类型擦除:每一个泛型类型都会自动提供一个原始类型,即删去类型参数后的泛型类型名,并将类型变量替换为限定类型 ( 无限定的变量用 Object)

    • 翻译泛型表达式:当程序调用泛型方法时 , 如果擦除返回类型, 编译器插入强制类型转换

    • 翻译泛型方法

      • 类型擦除也会出现在泛型方法中,参数类型被擦除后只留下限定类型

      • 类型擦除会与多态发生冲突,需要编译器在类中生成一个桥方法

      • 在虚拟机中, 用参数类型和返回类型确定一个方法 。

  • 约束与局限性

    • 不能用基本类型实例化类型参数:类型擦除使得泛型类中含有Object类型的域,Object不能存储基本类型的值

    • 运行时类型查询只适用于原始类型

      • 虚拟机中的对象总有一个特定的非泛型类型 。 因此, 所有的类型查询只产生原始类型 。

      • 如instanceof不能测试泛型类,getClass方法返回的也是原始类型

    • 不能创建参数化类型的数组:原因类型擦除会导致类型错误

    • Varargs警告

      • 由于java 不支持泛型类型的数组 ,因此向参数个数可变的方法(实质是一个数组)传递一个泛型类型会得到一个警告

      • 可以采用两种方法来抑制这个警告 。 一种方法是为包含调用的方法增加注解 @SuppressWamings("unchecked") 或者在 Java SE 7 中, 还 可 以 用 @ SafeVarargs 直 接 标 注

    • 不能实例化类型变量:可以通过反射调用 Clasmewlnstance 方法来构造泛型对象

    • 不能构造泛型数组:同样可以利用反射

    • 泛型类的静态上下文中类型变量无效

    • 不能抛出或捕获泛型类的实例

  • 泛型类型的继承规则

  • 通配符类型(略)

    • 通配符概念:允许类型参数变化

  • 反射和泛型

    • 泛型Class类

      • Class类是泛型的,String.class实际上是Class<String>的对象(唯一对象)

    • 使用Class<T>参数进行类型匹配

 

 

集合

  • java集合框架

    • 将集合的接口与实现分离

    • Colletion接口

      • 集合类的基本接口是Colletion接口

      • 基本方法:add(),iterator()返回迭代器对象

    • 迭代器(Iterator接口)

      • next()返回下一个元素

      • remove()删除上次调用next()返回的元素

      • 调用remove()前必须调用next()

    • 泛型实用方法

      • Colletion接口声明了许多有用的方法,所有的实现类都必须提供这些方法(具体方法声明见API)

    • 集合框架中的接口

      • 两个基本接口:Colletion和Map

      • List接口:有序集合,可用迭代器访问或使用整数索引访问(随机访问)

      • Set接口:等同于Colletion接口,但不允许增加重复元素

  • 具体的集合

    • 链表

      • java中的链表都是双向的

      • ListIterator接口提供了反向遍历链表的方法

      • 迭代器并发修改链表会产生异常

      • 随机访问耗费时间多

    • 数组列表

      • 随机访问快

      • Vector是同步的,线程安全;ArrayList不是同步的。如果由单线程访问,Vector同步会耗费大量的时间,因此不需要同步时使用ArrayList

    • 散列表

      • 散列表为每个对象计算一个整数(散列码),不同数据域的对象将产生不同的散列码

      • java中散列表由链表实现

      • 散列冲突:桶被占满,需要和桶内所有对象比较

      • 再散列:当装填因子达到某个值时,当前散列表用双倍的桶数自动进行再散列

    • 树集

      • 树集是有序的

      • 要使用树集必须能比较元素,即这些元素必须实现Comparable接口

    • 队列与双端队列

      • 队列可以让人们有效地在尾部添加一个元素, 在头部删除一个元素

      • 有两个端头的队列, 即双端队列, 可以让人们有效地在头部和尾部同时添加或删除元素

      • 不支持在队列中间添加元素

    • 优先级队列

      • 优先级队列 ( priority queue ) 中的元素可以按照任意的顺序插人, 却总是按照排序的顺序进行检索 。

      • 优先级队列使用堆数据结构

  • 映射

    • 基本映射操作

      • java中映射的两个实现:HashMap和TreeMap

      • HashMap对键进行散列,TreeMap用键对元素进行排序

      • 键必须是唯一的,不能对同一个键存放两个值

    • 映射视图:键集 、 值集合 (不是一个集 ) 以及键 / 值对集

    • 弱散列映射:解决键/值的回收问题

  • 视图与包装器

    • 轻量级集合包装器

    • 子范围

    • 不可修改的视图

    • 同步视图

    • 受查视图

  • 算法

  • 遗留的集合

 

 

图形程序设计

  • Swing概述

    • Swing是不对等基于GUI工具箱

    • Swing拥有丰富便捷的用户界面元素集合

    • Swing对底层平台依赖很少,因此与平台相关的bug很少

    • Swing给不同平台用户一致的观感

  • 创建框架

    • java顶层窗口被称为框架(frame)

    • Swing的框架类是JFrame,扩展于AWT库的Frame类

    • JFrame是极少数不绘制在面板上的Swing组件之一,其修饰部件由用户的窗口系统绘制

    • Swing组件必须由事件分派线程进行配置

    • 定义关闭框架的响应动作:setDefaultCloseOperation ( JFrame.EXIT_0N_CL0SE) ;

    • 显示框架:setVisible(true)

  • 框架定位

    • setLocation和setBounds设置框架的位置

    • setlconlmage 用于告诉窗口系统在标题栏、 任务切换窗口等位置显示哪个图标 。

    • setTitle 用于改变标题栏的文字 。

    • setResizable 利用一个 boolean 值确定框架的大小是否允许用户改变

    • 框架属性:一个获取 / 设置方法对被称为一种属性

    • 确定合适的框架大小

  • 在组件中显示信息

    • JFrame.add()向框架添加组件

    • 绘制组件:JComponent或Jpanl

  • 详见API

 

 

事件处理

  • 事件处理基础

    • java将事件信息封装在一个事件对象中,java中所有事件对象派生于java.util.EventObject类

    • 不同的数据源产生不同的事件对象

    • AWT事件处理机制的概要:

      • 监听器对象是一个实现了特定鉴定器接口的类实例

      • 事件源是一个能够注册监听器对象并发送事件对象的对象

      • 当事件发生时,事件源将事件对象传递给所有注册的监听器

      • 监听器对象利用事件对象中的信息决定如何对事件做出响应

    • 为了实现ActionListener接口,监听器类必须实现actionPerformed方法,该方法接收一个ActionEvent对象

    • 简洁地指定监听器:使用lambda表达式

      exitButton.addActionListener (event -> System.exit(O)) ;

    • 适配器类

      • 捕获事件需要有合适的监听器对象

      • 每个AWT监听器接口都有对应的适配器类,适配器类实现了监听器接口的所有方法,但每个方法没有做任何事情

      • 可以通过扩展适配器类来指定对某些事件的响应动作 , 而不必实现接口中的每个方法

  • 动作(详见API)

  • 鼠标事件(详见API)

  • AWT事件继承层次

    • 语义事件和底层事件

      • AWT将事件分为底层事件和语义事件

      • 语义事件是表示用户动作的事件

      • 底层事件是形成语义事件的事件

 

 

用户界面组件Swing

  • Swing和MVC(模型-视图-控制器)设计模式

    • 设计模式

      • 设计模式是一种结构化的方法

      • AWT和Swing中使用的设计模式

        • 模型-视图-控制器模式

        • 容器和组件是 “ 组合 ( composite ) ” 模式

        • 带滚动条的面板是 “ 装饰器( decorator ) ” 模式

        • 布局管理器是 “ 策略 ( strategy ) ” 模式

    • 模型-视图-控制器模式

      • MVC模式实现三个独立的类:

        • 模型:存储内容

        • 视图:显示内容

        • 控制器:处理用户输入

      • 模型必须实现改变内容和查找内容的方法 。模型是完全不可见的

  • 布局管理概述(详见API)

    • 边框布局(JFrame默认布局)

    • 流布局(Jpanel默认布局)

    • 网格布局

  • 文本输入

    • 文本域(JTextFeild):只允许接受单行文本输入

    • 文本区(JTextArea):能接受多行输入

    • JPassword :也只能接收单行文本的输人, 但不会将输入的内容显示出来 。

    • 标签和组件

    • 滚动窗格

  • 选择组件

    • 复选框(JCheckBox)

    • 单选钮(JRadioButton)

    • 边框

    • 组合框

    • 滑动条

  • 菜单

    • 菜单创建:

      • 创建菜单栏:new JMenuBar()

      • 创建菜单对象; new JMenu()

      • 将菜单添加到菜单栏

      • 向菜单对象添加菜单项,分隔符和子菜单

  • 对话框(详见API)

 

 

部署Java应用

  • JAR文件

    • Java归档文件(JAR)c用于将应用程序打包,JAR文件可以既可以包含类文件,包含其他类型的文件。

    • JAR 文件是压缩的 ,使用了大家熟悉的 ZIP 压缩格式

    • 创建JAR文件:jar options File File2 ...

    • 清单文件:用于描述归档特征,清单文件被命名为MANIFEST.MF,位于META-INF子目录(清单文件的最后一行必须以换行符结束)

    • 可执行JAR文件:使用 jar 命令中的 e 选项指定程序的入口点或在清单中指定应用程序的主类

 

 

并发

  • 什么是线程

    • 使用线程给其他任务提供机会

      • 将耗时的任务放置在独立的线程中

      • 在单独线程中执行任务的过程:

        • 将任务代码移到实现了Runnable接口类的run方法中(Runnable是函数式接口,可以使用lambda表达式建立实例)

          Runnable r = ()-> { task code } ;

        • 由Runnable创建一个Thread对象

          Thread t = new Thread(r);

        • 启动线程

          t.start();

      • 也可以通过构建Thread类的子类定义一个线程(不推荐)

      • 不要调用Thread类或Runnable类对象的run方法。直接调用run方法只会执行同一个线程中的任务,不会创建新线程。应该调用Thread.start()方法,该方法创建一个执行run方法的新线程

  • 中断线程

    • 线程终止:线程的run方法执行方法体中的最后一条语句后,并经由执行return语句返回时;或出现了在方法中没有捕获的线程

    • 没有可以强制线程终止的方法,可以使用interrupt方法请求终止线程

    • 每个线程都具有boolean的中断状态,当对一个线程调用 interrupt 方法时, 线程的中断状态将被置位

    • 如果线程被阻塞就无法检测中断状态

    • 对InterruptedException的处理:

      • 在 catch 子句中调用 Thread.currentThread ().interrupt() 来设置中断状态

      • 用 throws InterruptedException 标记你的方法, 不采用 try 语句块捕获异常

  • 线程状态

    • 线程有以下6种状态:

      • New(新创建):当用new操作服创建一个线程时,该线程还没有开始运行,状态为New

      • Runnable(可运行):一旦调用start方法,线程就处于Runnable状态。

        • 一个可运行线程可能正在运行也可能没有运行(取决于操作系统给线程提供运行的时间)

        • 一旦一个线程开始运行 , 它不必始终保持运行 。 事实上, 运行中的线程被中断 , 目的是为了让其他线程获得运行机会

        • 线程调度由操作系统决定

      • Blocked(被阻塞):当线程试图获取一个对象的内部锁而该锁被其他线程占有时,线程进入阻塞状态;当其他线程释放该锁,并且线程调度器允许本线程获得它的时候,该线程将变成非阻塞状态

      • Waiting(等待):当线程等待另一个线程通知调度器一个条件时, 它自己进入等待状态

        • 当线程处于被阻塞或等待状态时 , 它暂时不活动 。 它不运行任何代码且消耗最少的资源 。 直到线程调度器重新激活它

      • Timed waiting(计时等待):调用有超时参数的方法将导致线程进人计时等待

      • Terminated(被终止):

        • 因为run方法正常退出而自然死亡

        • 因为一个没有捕获的异常终止了run方法而意外死亡

  • 线程属性

    • 线程优先级:每个线程都有一个优先级,一个线程默认继承父线程的优先级;使用setPriority方法设置优先级

    • 守护线程:线程调用stDaemon方法可以转换成守护线程,守护线程的唯一用途是为其他线程提供服务

    • 未捕获异常处理器

      • 线程的run方法不能抛出任何受检异常,但非受检异常会导致线程终止

      • 在线程死亡之前, 异常被传递到一个用于未捕获异常的处理器 。该处理器必须属于一个实现 Thread.UncaughtExceptionHandler 接口的类

  • 同步

    • 当两个线程存取相同的对象并调用了修改该对象状态的方法时,这种情况成为竞争条件

    • 锁对象

      • 防止代码块受并发访问的干扰的两种方法:

        • synchronized关键字:自动提供锁以及相关条件

        • ReentrantLock 类:创建ReentrantLock实例,lock()方法封锁锁对象,unlock()方法释放锁对象

      • 锁是可重入的,线程可以重复地获得以拥有的锁

      • 锁保持一个持有计数 (holdcount) 来跟踪对 lock 方法的嵌套调用

      • 公平锁:优先选择等待时间最长的线程

    • 条件对象

      • 条件对象用于管理已经获得锁但是不能做有用工作的线程

      • 条件对象在条件不满足时调用await()方法,阻塞当前线程并释放锁,直到另一个线程调用同一条件的signalAll方法(激活)

      • 如果没有线程唤醒等待的线程,会出现死锁

      • 调用 signalAll 不会立即激活一个等待线程 。 它仅仅解除等待线程的阻塞,仍要竞争锁

    • synchronized 关键字

      • java对象自带内部锁,

      • 如果一个方法用 synchronized 关键字声明, 那么对象的锁将保护整个方法

      • 内部对象锁只有一个相关条件:wait()方法添加线程到等待集,notifyAll()/notify()解除阻塞

      • 可以将静态方法声明为synchronized,该方法会或得相关类的对象内部锁

      • 内部锁的局限:

        • 不能中断一个正在试图获得锁的线程

        • 试图获得锁时不能设定超时

        • 每个锁仅有单一的条件

    • 同步阻塞:通过同步阻塞可以获得锁

      synchronized ( obj )//同步阻塞的语法

       

      {

       

      }//获得obj的锁

    • 监视器概念

      • 监视器的特性

        • 监视器是只包含私有域的类

        • 每个监视器的对象有一个相关的锁

        • 使用该锁对所有的方法进行加锁

        • 该锁可以有任意多个条件

    • Volatile域:volatile 关键字为实例域的同步访问提供了一种免锁机制 。 如果声明一个域为 volatile ,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的 。

    • final变量:当域声明为final时可以安全地被访问,

    • 原子性

    • 死锁

    • 线程共享变量

    • 锁测试与超时

      • tryLock方法试图申请一个锁,成功获得返回true,失败返回false并且线程可以立即离开去做其他事情

      • 在调用tryLock时可以使用超时参数

      • lock不能被中断,如果一个线程在等待获得一个锁时被中断 , 中断线程在获得锁之前一直处于阻塞状态

      • 调用带有用超时参数的 tryLock , 那么如果线程在等待期间被中断, 将抛出InterruptedException 异常

    • 读/写锁

      • java.util.concurrent.locks包的两个锁类:ReentrantLock类和ReentrantReadWriteLock类

      • ReentrantReadWriteLock允许对读者线程共享访问,写者线程互斥访问

      • 使用读/写锁的步骤;

        • 构造一个ReentrantReadWriteLock对象 :

          private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock ();

        • 抽取读锁和写锁 :

          private Lock readLock = rwl.readLock();

           

          rivate Lock writeLock = rwl.writeLock();

           

          p
        • 对所有的获取方法加读锁 :

          public double getTotalBalance()

          {

          readLock.lock();

          try { . . }

           

          }

          finally { readLock.unlock() ;

          }

        • 对所有的修改方法加写锁 :

          public void transfer ( ... )

          {

          writeLock.lock();

          writeLock

          try{ ... }

          finally

          {.unlock () ; }

           

          }

  • 阻塞队列

    • 使用一个或多个队列解决线程问题

    • 生产者线程向队列插人元素, 消费者线程则取出它们 。

    • 当试图向队列添加元素而队列已满 , 或是想从队列移出元素而队列为空的时候, 阻塞队列 ( blocking queue ) 导致线程阻塞 。

  • 线程安全的集合

    • 高效的映射、集和队列

      • java . util . concurrent 包提供了映射 、 有序集和队列的高效实现 : ConcurrentHashMap 、ConcurrentSkipListMap > ConcurrentSkipListSet 和 ConcurrentLinkedQueue 。

    • 映射条目的原子更新

      • 使用 replace 操作 , 它会以原子方式用一个新值替换原值

  • Callable与Future

    • Callable类似于Runnable,但是有返回值

    • Future 保存异步计算的结果

  • 执行器

    • 如果程序中创建了大量的生命期很短的线程 , 应该使用线程池

    • 使用线程池可以减少并发线程的数目

    • 执行器 ( Executor ) 类有许多静态工厂方法用来构建线程池

    • 将 Runnable 对象交给线程池 , 就会有一个线程调用 run 方法。 当 run 方法退出时 , 线程不会死亡, 而是在池中准备为下一个请求提供服务 。

    • 线程池

      • newCachedThreadPool方法构建了一个线程池 , 对于每个任务, 如果有空闲线程可用 , 立即让它执行任务, 如果没有可用的空闲线程, 则创建一个新线程 。

      • newFixedThreadPool方法构建一个具有固定大小的线程池。如果提交的任务数多于空闲的线程数, 那么把得不到服务的任务放置到队列中。当其他任务完成以后再运行它们。

      • newSingleThreadExecutor 是一个退化了的大小为 1 的线程池: 由一个线程执行提交的任务, 一个接着一个 。

      • 调用submit方法将Runnable对象或Callable对象提交给 ExecutorService

      • 当用完一个线程池的时候, 调用 shutdown启动线程池的关闭序列

  • 同步器

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值