0%

郑莉版输入输出流课件

在Java中将信息的输入与输出过程抽象为I/O流
I/O流一旦被创建就会自动打开
通过调用close方法,可以显式关闭任何一个流
如果流对象不再被引用,Java的回收机制也会隐式地关闭它
-w687

读写数据的方法

不论数据从哪来,到哪去,也不论数据本身是何种类型,读写数据分方法大体上都是一样的
-w524

I/O流的分类

  • 从流的方向划分
  • 输入流
  • 输出流
  • 从流的分工划分
  • 节点流
  • 处理流
  • 从流的内容划分
  • 面向字符的流
  • 面向字节的流

自己补充了解:
节点流和处理流的区别
-w568

java.io 包的顶级层次结构

面向字符的流: 专门用于字符数据
面向字节的流: 用于一般目的
-w586

面向字符的流

  • 针对字符数据的特点进行过优化,提供一些面向字符的有用特性
  • 源或目标通常是文本文件

    实现内部格式和文本文件中的外部格式之间的转换

    -w684

    面向字符的抽象类Read和Writer

  • java.io包中所有流的抽象基类
  • Reader提供了输入字符的API
  • Writer提供了输出字符的API
  • 它们的子类又分为两大类
  • 节点流: 从数据源读入数据或往目的地写出数据
  • 处理流: 对数据执行某种处理
  • 多数程序使用这两个抽象类的一系列子类来读入/写出文本信息
  • 例如: FileRader/FileWriter用来读写文本文件

处理流和节点流一览:
-w670

面向字节的流

  • 数据源或目标中含有非字符数据,必须用字节流来输入/输出。
  • 通常被用来读写诸如图片、声音之类的二进制数据
  • 绝大多数数据是被存储为二进制文件的,世界上的文本大约只能占到2%,通常二进制文件比含有相同数据量的文本文件小得多。

抽象类

要点提示: 父类中定义了相关子类的共同行为。接口可以拥有定义类的共同行为(包括非相关的类)

抽象类

要点提示: 抽象类不可以用于创建对象。抽象类可以包含抽象方法,这些方法将在具体的子类中实现。

  • 抽象类定义: 类的设计应该确保父类包含它的子类的共同特征。有时候,一个父类设计得非常抽象,以至于它都没任何具体的实例。这样的类称为抽象类。

  • 抽象方法:
    -w1084

  • 抽象类和常规类很像,但是不能使用new操作符创建它的实例。抽象方法只有定义而没有实现。他的实现由子类实现。一个包含对象方法的类的必须声明为抽象类。

  • 抽象类的沟造方法定义为protected,它只能被子类使用。创建一个具体的子类的实例时,它的父类的构造方法被调用以初始化父类中定义的数据域。

为何要使用抽象方法

使用抽象方法以后,JVM在运行时可以根据对象的类型动态地决定调用哪一个方法。

抽象类的几点说明

  • 抽象方法不能包含在非抽象类中。如果抽象父类的子类不能实现所有的抽象方法,那么子类也必须定义为抽象的。即在抽象类扩展的非抽象子类中,必须实现所有的抽象方法。另外,抽象方法是非静态的。
  • 抽象类不能使用new关键字来初始化。但是仍然可以定义它的构造方法,这个构造方法在它的子类的构造方法中调用。
  • 抽象方法的类必须是抽象的。但是,可以定义一个不包含抽象方法的抽象类。这种情况下,不能使用new创建该类的实例。这种类是用来定义新子类的基类的。
  • 子类可以覆盖分类的方法并将它定义为abstract。
  • 即使子类的父类是具体的,这个子类也可以是抽象的。
  • 不能使用new从一个抽象类创建一个实例。但是抽象类可以用作一种数据类型。
    下面语句用来创建一个元素是GeometricObject类型的数组:
    GeometricObject[] objects = new GeometricObject[10];
    然后可以创建一个GeometricObject的实例,并将它的引用赋值给数组。
    object[0] = new Circle();// Circle是GeometricObject的子类

示例学习: 抽象的Number类

要点提示: Number类是数值包装类、BigInteger以及BigDecimal的父类。

示例学习: Calendar和GregorianCalendar

要点提示: GregorianCalendar是抽象类Calendar的一个具体子类。

一个Date的实例表示以毫秒为精度的特定时刻。Calendar是一个抽象的基类,可以提取出详细的日历信息。例如: 年、月、日、小时、分钟和秒。
GregorianCalendar是一个支持公历的类。
Calendar类中的add方法是抽象的,因为它的实现依赖于某个具体的日历系统。

可以使用new GregorianCalendar()利用当前时间构造一个默认的GregorianCalendar对象,可以使用GregorianCalendar(year, month, date)利用指定的year、month和date(日)构造一个GregorianCalendar对象。参数month是基于0的,即0代表1月

在Calendar类中定义的get(int field)方法在从Calendar类中提取日期和时间信息方面是很有用的。日期和时间域都被定义为常量。

接口

要点提示: 接口是一种与类相似的结构,只包含常量和抽象方法

接口在很多方面都与抽象类很相似,但是它的目的是指明相关或者不相关类的多个对象的共同行为。例如,使用正确的接口,可以指明这些对象是可比较的、可食用的,以及可克隆的。
定义接口:
-w975

在java中,接口被看作是一种特殊的类。就像常规类一样,每个接口都被编译为独立的字节码文件。使用接口或多或少有点像使用抽象类。即:

  • 可以使用接口作为引用变量的数据类型或类型转换的结果。
  • 不能使用new操作符创建接口的实例。

可以使用Edible接口来明确一个对象是否是可食用的。这需要使用implements关键字让对象的类实现这个接口来完成。

Comparable接口

要点提示: Comparable接口定义了compoareTo方法,用于比较对象。
接口的定义如下:
// Interface for comparing objects, defined in java.lang
package java.lang;
public interface Comparable
{ public int compareTo(E o);
}

Comparable 方法判断这个对象相对于给定对象o的顺序,并且当这个对象小于、等于或大于给定对象o时,分别返回负整数、0或正整数。

Comparable接口是一个泛型接口。在实现该接口时,泛型类型E被替换成一种具体的类型。

由于所有Comparable对象都有compareTo方法,如果对象是Comparable接口类型的实例的话,Java API中的java.util.Arrays.sort(Object[])方法就可以使用compareTo方法对数组中的对象进行比较和排序。

Cloneable接口

要点提示: Cloneable接口给出了一个可克隆的对象。

经常会出现需要创建一个对象拷贝的情况。为了实现这个目的,需要使用clone方法并理解Cloneable接口。

接口通常包括常量和抽象方法,但是Cloneable接口是一个特殊情况。在java.lang包中的Cloneable接口的定义如下:
package java.lang;
public interface Cloneable {
}

也就是说,这个接口是空的。
一个带空体的接口被称为标记接口(maker interface).一个标记接口及不包括常量也不包括方法。它用来表示一个类拥有某种特定的属性。实现Cloneable接口的类标记为可克隆的,而且它的对象可以使用在Object类中定义的clone()类方法。

Java库中的很多类(例如,Date、Calendar和ArrayList)实现Cloneable。这样这些类的实例可以被克隆。

为了定义一个自定义类来实现Cloneable接口,这个类必须覆盖Object类中的clone()方法。

浅复制、 深复制

意思是:
Object类中的clone方法将原始对象的每个数据域赋值给目标对象。如果一个数据域是基本类型,复制的就是它的值,例如,area(double 类型)的值从house1复制到house2.如果一个数据域是对象,复制的就只是该域的引用。例如,域whenBuilt是Date类,所以,它的引用被复制给house2。
因此,尽管,house1==house2为假,但是house1.whenBuilt==house2.whenBuilt为真。这称为浅复制(shallow copy)而不是深复制(deep copy),这意味着如果数据域是对象类型,那么复制的是对象的引用,而不是它的内容。

接口与抽象类

要点提示:一个类可以实现多个接口,但是只能继承一个父类。
接口的使用和抽象类的使用基本相似,但是,定义一个接口与定义一个抽象类有所不同。
-w1096

利用关键字extands,接口可以继承其他接口。这样的接口称为子接口(subinterface)。

接口可以扩展其他接口而不是类。一个类可以扩展它的父类同时实现多个接口。

所有的类共享一个根类Object,但是接口没有共同的根。与此类似,接口也可以定义一种类型。一个接口类型的变量可以引用实现该接口的类的实例。如果一个类实现了一个接口,那么这个接口就类似于该类的一个父类。可以将接口当作一种数据类型使用,将接口类型的变量转换为它的子类,反过来也可以。

注意:类名是一个名词。接口名可以是形容词或者名词。

设计指南:
-w1131

通常,推荐使用接口而非抽象类,因为接口可以定义非相关类共有的父类型。接口比类更灵活。

实例学习: Rational类

设计一个Rational类,用于表示和处理有理数。

类的设计原则

要点提示: 类的设计原则有助于设计出合理的类

内聚性

类应该描述一个单一的实体,而所有的类操作应该在逻辑上互相配合,只吃一个一致的目的。

如果一个实体担负太多的职责,就应该按各自的职责分成几个类

一致性

遵循标准Java程序设计风格和命名习惯。为类、数据域和方法选取具有信息的名字。通常的风格是将数据声明至于构造方法之前,并且将构造方法置于方法之前。

选择名字要保持一致。给类似的操作选择不同的名字并非良好的实践。

一般来说,应该具有一致性地提供一个公共无参构造方法,用于构建默认实例。如果一个类不支持无参的构造方法,要用文档写出原因。如果没有显式定义构造方法,即假定有一个空方法体的公共默认无参构造方法。

如果不想用户创建类的对象,可以在类中声明一个私有的构造方法,Match类就是如此

封装性

一个类应该使用private修饰符隐藏其数据,以免用户直接访问它。这使类更易于维护。

只在希望数据域可读的情况下,才提供get方法;也只希望数据域可更新的情况下,才提供set方法。

清晰性

类应该有一个很清晰的合约,从而易于解释和理解。

用户可以以各种不同组合、顺序,以及在各种环境中结合使用多个类。因此,在设计一个类时,这个类不应该限制用户如何以及何时使用该类;以一种方式设计属性,以容许用户按值的任何顺序和任何组合来设置;设计方法应该使得实现的功能与它们出现的顺序无关。

不应该声明一个来自其他数据域的数据域。
例如:
-w1122

完整性

类是为许多不同用户的使用而设计的。为了能在一个广泛的应用中使用,一个类应该通过属性和方法提供多种方案以适应用户的不同需求。

实例和静态

依赖于类的具体实例的变量或方法必须是一个实例变量或方法。

如果一个变量被类的所有实例所共享,那就应该将它声明为静态的。

如果方法 不依赖于某个具体的实例,那就应该将它声明为静态方法。

应该总是使用类名(而不是引用变量)引用静态变量和方法,以增强可读性并避免错误。

不要从构造方法中传入参数初始化静态数据。最好使用set方法改变静态数据域。

实例和静态是面向对象程序设计不可或缺的部分。数据域或方法要么是实例的,要么是静态的。不要错误地忽视了静态数据域或方法。

构造方法永远都是实例方法,因为它是用来创建具体实例的。一个静态变量或方法可以从实例中调用,但是不能从静态方法中调用实例变量或方法。

继承和聚合

继承和聚合之间的差异,就是is-a(是一种)和has-a(具有)之间的关系。
这里解释一下后者:
人具有名字,因此,可以使用聚合来对Person类和Name类之间的关系建模。

接口和抽象类

接口和抽象类都可以用于为对象指定共同的行为。

如何决定是采用接口还是抽象类?
同常,比较强的is-a关系清晰地描述了父子关系,应该采用类的继承关系来建模。弱的is-a关系,也称为is-kind-of(是一类)关系,表明一个对象拥有某种属性。弱的is-a关系可以使用接口来建模。

接口比抽象类更灵活,因为一个子类只能继承一个父类,但是却可以实现任意个数的接口。然而,接口不能具有具体的方法。可以结合接口和抽象类的优点,创建一个接口,使用一个抽象类来实现它。可以视其方便使用接口或抽象类。

有关重写的问题

重写和覆盖的关系

* 重写就是覆盖 *

需要注意的几点:

  • 仅当实例方法是可访问时,它才能被覆盖。因为私有方法在它的类本身以外是不能访问的,所以它不能被覆盖。如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系。
  • 与实例方法一样,静态方法也能被继承。但是,静态方法不能被覆盖。如果父类中定义的静态方法在子类中重新被定义,那么在父类中定义的静态方法将被禁藏。可以使用语法: 父类名.静态方法名(superClassName.staticMethodName)调用隐藏的静态方法。

    重写与重载

    不允许在子类中的一个方法具有和它父类中的方法完全相同的方法签名,但是返回值类型不同。这样会导致语法错误。

子类中与其父类中的方法同名但具有不同参数类型的方法被称为重载。

ArrayList类

要点提示:ArrayList 对象可以用于存储一个对象列表

用数组存储对象

数组可以用于存储一个一组对象,但是这个数组一旦创建,它的大小就固定了。Java提供ArrayList类来存锤不限定个数的对象。

ArrayList类

ArrayList是一种泛型类,具有一个泛型类型E。创建一个ArrayList时,可以指定一个具体的类型来替换E。
ArrayList类详细:
-w1127

创建一个ArrayList,并且将其引用赋值给变量cities。该ArrayList对象可以用于存储字符串。例如:
ArrayList cities = new ArrayList();

创建一个ArrayList并且将其引用赋值给变量dates。该ArrayList对象可以用于存储日期。
ArrayList dates = new ArrayList();

* 更新说明 *
-w1039

ArrayList和数组之间的异同

-w1171
-w1169

分别对数组和ArrayList进行排序:
-w1133

Java-对象转换和instanceof

要点提示:对象的引用可以类型转换为另外一种对象的引用,这称为对象转换。

对象转换

隐式转换

Object o = new Student();
Student类是Object类的一个子类

向上转换(upcasting)

总是可以将一个子类的实例转换为一个父类的变脸,称为向上转换,因为子类的实例永远是它的父类的实例。(不需要使用转换记号”(子类名)”)

向下转换(downcasting)

当把一个父类的实例转换为它的子类变量(称为向下转换)时,必须使用转换记号”(子类名)”进行显式转换,向编译器表明意图。

为使转换成功,必须确保要转换的对象是子类的一个实例。如果父类对象不是子类的一个实例,就会出现一个运行异常ClassCastException.

instanceof

instanceof 是java中的关键字

用法:

(A和B都是对象)
A instanceof B //用来确定A是不是B的一个实例

⚠️

*注意 *
对象成员访问运算符(.)优先于类型转换运算符。所以使用圆括号保证在点运算符(.)之前进行转换,例如:
((Circle)object).getArea();

另外,对基本类型值进行转换不同于对对象引用进行转换。转换基本类型值返回一个新的值,例如:
int age = 45;
byte newAge = (byte)age;// A new value is assigned to newAge

而转换一个对象引用不会创建一个新的对象。例如:
Object o = new Circle();
Circle c = (Circle)o; // No new object is created
现在,引用变量o和c指向同一个对象。

instanceof关键字用法

instanceof 是java的一个二元操作符,类似于==,>,<等操作符。
instanceof 是java的保留关键字,它的作用是测试它左边的对象是否是它右边的类的实例 ,返回boolean的数据类型

Java_多态

多态是同一个行为具有不同表现形式或形态的能力
多态就是同一个接口,使用不同的实例而执行不同的操作。

多态性的优点

  • 消除类型之间的耦合关系
  • 可替换性
  • 可扩充性
  • 借口性
  • 灵活性
  • 简化性

    多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

当使用多态方式调用方法时,首先检测父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class DuoTai {
public static void main(String[] args) {
show(new Cat()); // 以Cat对象调用show方法
show(new Dog()); // 以Dog对象调用show方法

Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是Cat的eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是Cat的work
}

public static void show(Animal a){
a.eat();
if (a instanceof Cat){ // 猫做的事情
Cat c = (Cat)a;
c.work();
}
else if (a instanceof Dog){ // 狗做的事情
Dog c = (Dog) a;
c.work();
}

}
}

abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
@Override
public void eat(){
System.out.println("吃鱼");
}
public void work(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
@Override
public void eat(){
System.out.println("吃骨头");
}
public void work(){
System.out.println("看家");
}
}

虚函数

虚函数的存在是为了多态
Java中其实并没有虚函数的概念,它的普通函数就相当于C++的虚函数,动态绑定是Java的默认行为。如果Java中不希望某个函数具有虚函数特性,可以加上final关键字编程非虚函数

重写

也就是子类能够重写父类的方法。
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,则必须使用关键字super。

PIL包中Image模块的convert()函数的具体使用

RGB到灰度转换公式

RGB到灰度转换公式:Y’ = 0.299 R + 0.587 G + 0.114 B

convert 函数

convert函数,用于不同模式之间的转换。PIL中有9种不同模式,分别为1,L,P,RGB, RGBA, CMYK, YCbCr,I,F.

其中模式”1”为二值图像,每个像素用8bit,0表示黑,255表示白。

其中“L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。在PIL中,从模式“RGB”转换为“L”模式是按照下面的公式转换的。
L = 0.299 R + 0.587 G + 0.114 B

static 详解

实例变量

是一个java对象就有一个,100个对象就有100个。
实例变量存储在java对象内部,在堆内存中,在构造方法执行的时候初始化

静态变量

所有对象的该变量值一样,这种特征属于类级别的特征,可以提升为整个模版的特征,可以在变量前添加static关键字修饰

静态变量在类加载的时候初始化,不需要创建对象,内存就开辟了

静态变量存储在方法区内存当中

静态变量访问时直接用类名访问,而不用实例对象名

总结:

什么时候成员变量声明为实例变量?
- 所有对象都有这个属性,但是这个属性的值会随着对象的变化而变化(不同的对象的这个属性具体的值不同)
什么时候成员变量声明为静态变量呢?
- 所有对象都有这个属性,并且所有对象的这个属性的值是一样的,建议定义为静态变量,节省内存的开销。

静态变量在类加载的时候初始化,内存在方法去内存中开辟/访问的时候不需要创建对象,直接使用”类名.静态变量名”的方式访问。(也可以用引用的方式访问,但是会被警告)

所有静态的数据都可以采用类名.,也可以采用引用.,但是建议采用类名.的方式访问。
采用引用.的方式访问的时候,即使引用是null,也不会出现空指针异常。因为访问静态的数据不需要对象的存在。

static关键字:
- 翻译为静态
- 修饰的方法是静态方法
- 修饰的变量是静态变量
- 所有static修饰的元素称为静态的,都可以可以采用类名.,也可以采用引用.,但是建议采用类名.的方式访问。
- static修饰的所有元素都是类级别的特征,和具体的对象无关

Java-BigInteger和BigDecimal类

要点提示:Java-BigInteger和BigDecimal类可以用于表示任意大小和精度的整数或者十进制数

BigInteger

BigInteger的实例可以用来表示任意大小的整数。可以使用new BigInteger(String) 和new BigDecimal(String)来创建 BigInteger 和 BigDecimal的实例,使用add、subtract、multiple、divide和remainder方法完成算术运算,使用compareTo方法比较两个大数字。

对BigDecimal对象的精度没有限制。如果结果不能终止,那么divide方法会抛出ArithmeticException异常。但是,可以使用重载的divide(BigDecimal d, int scale, int roundingMode)方法来指定尺度和舍入方法来避免这个异常。这里的scale是指小数点后最小的整数位数。