Java第五天 2012年5月11日
一、复习
1、封装
该隐藏隐藏,该公开的公开
属性隐藏,同时提供get/set方法
有些方法应该隐藏
方法声明公开,实现隐藏。实现的改变对架构的影响最小
2、继承
一般->特殊
单继承:一个类最多只能有一个直接父类。类之间可以形成树状关系
根据访问权限,子类如果可以访问父类的属性和方法,就能继承
private 私有 不能继承
default 本类+同包 同包子类可以继承,不同包子类不能继承
protected 本类+同包+不同包子类 可以继承
public 公开 可以继承
方法的覆盖(Override):
方法名相同,参数表相同,返回值相同,访问修饰符比父类相同或更宽,抛出的异常不能比父类更宽
继承关系下对象的构造过程
1)递归的构造父类对象
2)分配本类空间
3)初始化本类属性
4)调用本类的构造方法
super:
super. ->父类对象,访问父类被覆盖的方法或者父类被遮盖的属性
super() ->用在构造方法时,用来指明调用父类的哪个构造方法,放在构造方法的第一行,默认调用父类无参构造方法
3、多态
编译时多态:方法的重载
运行时多态:
子类对象当作父类对象来用!!!屏蔽同一父类下,不同子类差异
Animal a = new Dog();
允许引用类型和对象类型不同,但要求引用类型是对象类型的父类。
对象类型代表了对象自身客观的实际类型,引用类型代表了主观上把对象当作什么类型来看待。
引用类型:编译时类型,主观类型
对象类型:运行时类型,客观类型
1)对象运行时类型不变
2)只能对对象调用其编译时类型定义的方法
3)运行时根据对象类型去匹配对象类型中覆盖之后的方法
Super s1;
Sub s2;
s1=s2;
s2=s1; //error , s2=(Sub)s2
强制类型转换:当我们把父类的引用赋值给子类引用的时候,需要强制类型转换。强制类型转换失败:类型转换异常.
为了避免类型转换异常,使用instanceof判断
引用 instanceof 类名 引用指向的对象的类型与后面的类名是否兼容
多态的作用:通用编程
我们可以把不同子类的对象都当作父类对象看待,评比不同子类的差异。
二、CARP(组合/聚合复用原则)
实现代码重用最好的方法不是继承
两种复用
白盒复用,也就是继承复用,破坏封装,父类中的可以被子类访问到的就可以被继承,这样会有些不需要的内容被继承下来,所以这种方式不太好。
黑盒复用,也叫组合复用,也就是把要复用代码的类的对象作为本类中的一个属性,然后再通过方法的委托来实现由选择的复用。
方法的委托就是在本类的方法内部通过该类的对象调用要使用类的方法,不破坏封装。
注意:尽量用组合复用替代继承复用。
三、多态
1、多态用于参数,可以在方法的参数中传入其父类类型,在运行时会根据实际的运行时类型来在方法中进行相应的操作。
多态一般用在方法的参数上
void feed(Animal a){
a.eat();
}
调用的时候feed(new Dog()); //运行时,调用的是Dog类中覆盖的eat()方法
2、多态用于返回值,可以在方法的返回值类型上是用其实际返回值的父类型,在使用其返回值时也不关心其实际类型。
public static Animal getAnimal(int type){
if (type==0) return new Dog();
else return new Cat();
}
屏蔽子类差异,可扩展(只修改方法的实现,不必修改方法的声明)
3、Animal a = new Dog();
a.age; //访问属性是没有多态的,访问的是引用的age属性
a.eat(); //调用Dog类中覆盖Animal类中的eat()方法,多态
4、Animal a = new Dog();
method(a);
运行结果调用参数是Animal类对象的那个method()方法
方法的重载只看引用类型,跟引用指向的对象类型没有关系
四、对象的比较
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2; -> false 判断两个引用是否指向同一对象,即地址是否相同
s1.equls(s2); -> true 判断两个引用指向的对象的内容是否相同
练习:
(继承,多态)
银行的客户分为两类,储蓄账户(SavingAccount)和信用账户(CreditAccount),区别在于储蓄账户不允许透支
而信用账户可以透支,并允许用户设置自己的透支额度.
注意:CreditAccount需要多一个属性 ceiling 透支额度
为这两种用户编写相关的类
同时要求编写Bank类,属性:
1.当前所有的账户对象的集合,存放在数组中
2.当前账户数量
方法:
1.用户开户,需要的参数:id,密码,密码确认,姓名,身份证号码,邮箱,账户类型,返回新创建的Account对象
提示:用s1.equals(s2) 可以比较s1,s2两个字符串的值是否相等.账户类型是一个整数,为的时候表示储蓄账户,为1的时候表示信用账户
2.用户登录,参数:id,密码 返回Account对象
3.用户存款,参数:id,存款数额,返回修改过的Account对象
4.用户取款,参数:id,取款数额,返回修改过的Account对象
5.设置透支额度 参数:id,新的额度 ,返回修改过的Account对象.这个方法需要验证账户是否是信用账户
用户会通过调用Bank对象以上的方法来操作自己的账户,请分析各个方法需要的参数
另外,请为Bank类添加几个统计方法
1.统计银行所有账户余额总数
2.统计所有信用账户透支额度总数
写个主方法测试你写的类
Java第六天 2012年5月12日
修饰符
一、static
修饰属性,方法,代码块
1、静态属性:全类公有,称为类变量
那么这个属性就可以用 类名.属性名 来访问
(共有的类变量与对象无关,只和类有关)
类加载:虚拟机通过I/O流把一个类的信息从字节码文件中读入虚拟机并保存起来
一个类只会加载一次
类变量,会在加载时自动初始化,初始化规则和实例变量相同。
注意:类中的实例变量是在创建对象时被初始化的,被static修饰的属性,也就是类变量,是在类加载时被创建并进行初始化,类加载的过程是进行一次。也就是类变量只会被创建一次。
2、静态方法:
使这个方法成为整个类所公有的方法,可以用 类名.方法名 直接访问
注意:static修饰的方法,不能直接访问(可以通过组合方式访问)本类中的非静态(static)成员(包括方法和属性)
本类的非静态(static)方法可以访问本类的静态成员(包括方法和属性),可以调用静态方法。
静态方法要慎重使用。在静态方法中不能出现this关键字,因为这是针对对象而言的。
java中的main方法必须写成static的,因为,在类加载时无法创建对象,静态方法可以不通过对象调用。
所以在类加载时就可以通过main方法入口来运行程序。
注意:父类中是静态方法,子类中不能覆盖为非静态方法。
在符合覆盖规则的前提下,在父子类中,父类中的静态方法可以被子类中的静态方法覆盖,但是没有多态。
使用引用调静态方法,相当于使用引用的类型去调用静态方法。(在使用对象调用静态方法是其实是调用编译时类型的静态方法)
3、初始代码块
在定义属性的位置上,在任何方法之外,定义一个代码块
动态初始代码块:在初始化属性之前调用初始化代码块 {……}
静态初始代码块:在类加载时运行 static{……} 只被运行一次,往往用作一个类的准备工作
二、一个类在什么时候被加载?时机 (延迟加载,能不加载就不加载)
(1)new 一个对象的时候,加载
(2)没有创建对象,访问类中静态成员(方法和属性),加载
(3)声明一个类的引用,不加载
(4)创建子类,先加载父类,再加载子类
(5)父类中的公开静态方法,子类继承,使用子类的类名调用此方法,加载父类
class Super{
public static m(){}
}
class Sub extends Super{}
在主函数中运行以下代码:
Sub.m(); //加载了父类之后,虚拟机已经知道m()方法的调用了,就不会再加载子类,延迟加载
(6)没有创建对象,访问类中静态常量(能计算出结果的常量,在编译的时候会用计算出来的结果替换表达式),不加载
没有创建对象,访问类中静态常量(不确定的值),加载
(7)CoreJava day16
三、设计模式(编程套路)
GOF(Group Of Four)*模式 23种
1、单例模式 Singleton:
class A{
private static A a = new A(); //私有静态的实例变量指向自己,在类加载时创建唯一对象
public static A newInstance(){ //提供公开静态的访问点,回返唯一实例
return a;
}
private A(){} //私有的构造方法,防止滥用
}
2、不变模式 :
便于实例共享,减少对存储空间的消耗
String类采用了不变模式
字符串中的内容是不变的
String a1 = "123"; //系统会先去串池中找"123",找到,就共享使用一个,没找到就在串池中创建一个
String a2 = new String("123"); //在堆空间中创建"123"
池化的思想,把需要共享的数据放在池中(节省空间,共享数据)
只有String类可以用“”中的字面值创建对象。
在String类中,以字面值创建时,会到串池空间中去查找,如果有就返回串池中字符串的地址,并把这个地址付给对象变量。
如果没有则会在串池里创建一个字符串对象,并返回其地址付购对象变量,当另一个以字面值创建对象时则会重复上述过程。
如果是new在堆空间中创建String类的对象,则不会有上述的过程。
a2=a1.intern(); //返回字符串在串池中的引用
消极方面:字符串连接“+”,产生很多的中间对象
StringBuffer类,字符串是可变的
s.append("A"); //连接字符串,不创建中间对象
大量字符串连接的时候用StringBuffer取代String
四、final
修饰变量,方法,类
1、修饰变量
被fianl修饰的变量就是常量(常量名大写),一旦赋值不能改变
修饰局部变量:修饰基本数据类型 -> 变量的值不能改变
修饰引用 -> 引用只能指向固定的对象
修饰实例变量:默认值不生效,可以再赋值
有两次赋值机会:初始化变量的时候 final int a = 20; 对于直接在初始化时赋值,final修饰符常和static修饰符一起使用,避免浪费空间
构造方法中设置 this.a = a;
但是不能同时使用这两种方法
在一个对象完成创建的时候,对象中的所有final属性必须都完成赋值
类变量可以是final的,也有两次赋值机会 :定义变量的时候就赋值 ; 静态初始代码块中
2、修饰方法
不能被子类覆盖
从面向对象的角度理解,可以保持操作的稳定性
3、修饰类
不能被继承
在树状单继承关系中,final类是树叶节点
在一个final类中的所有方法,默认都是final的
注意:final,不能用来修饰构造方法。
在父类中如果有常量属性,在子类中使用常量属性时是不会进行父类的类加载。
静态常量如果其值可以确定,就不会加载该类,如果不能确定则会加载该常量所在的类。
class Super{
private final void m(){} //用final可以证明出private的方法不继承给子类
}
class Sub extends Super{
public void m(){} //不是方法的覆盖
}
五、abstract 抽象的
修饰类和方法
1、修饰类 -> 抽象类
不能创建对象,可以声明引用,并通过引用调用类中的方法
主要用于被子类继承的,可以用父类引用指向子类对象
2、修饰方法
只有声明,没有实现,用“;”代替“{ }”
需要子类继承实现(覆盖)。
如果一个类中有抽象方法,那么这个类必须是抽象类。
抽象类中不一定有抽象方法
注意:父类是抽象类,其中有抽象方法,子类继承父类,必须把父类中的所有抽象方法都实现(覆盖)了,子类才有创建对象的能力,
否则子类也必须是抽象类。
抽象类中可以有构造方法,是子类在构造子类对象时需要调用的父类(抽象类)的构造方法。
抽象类的合理性:
没有抽象类的实例,只有抽象类子类的实例
抽象方法,定义和实现分离
抽象(abstract)方法代表了某种标准,定义标准,定义功能,在子类中去实现功能(子类继承了父类并需要给出从父类继承的抽象方法的实现)。
方法一时间想不到怎么被实现,或有意要子类去实现而定义某种标准,这个方法可以被定义为抽象。(abstract)
六、三个修饰符都能修饰方法(不包含构造方法)
1、构造方法在创建对象的时候使用,如果是static,那么只会在加载类的时候调用一次
构造方法不能被继承(final),谈不到覆盖,更不会由子类实现(abstract)
2、final和abstract,private和abstract,static和abstract,这些是不能放在一起的修饰符
因为abstract修饰的方法是必须在其子类中实现(覆盖),才能以多态方式调用,以上修饰符在修饰方法时子类都覆盖不了这个方法。
final是不可以覆盖,private是不能够继承到子类,所以也就不能覆盖。
static是可以覆盖的,但是在调用时会调用编译时类型的方法(引用类型的方法),因为调用的是父类的方法,而父类的方法又是抽象的方法,不能调用。
所以上的修饰符不能放在一起。
作业:(语言高级特性,三个修饰符)
1.修改Account类,银行用户的账号(id)是自动生成的,初始值为100000,第一个开户的用户id为100001,第二个为100002,依此类推.
提示:构造对象的时候采用static属性为id赋值
2.对于Account类,有两个方法,存款方法和取款方法,请修改这两个方法.
存款方法改为不允许子类修改
取款方法根据不同的子类而不同,因此,改为抽象方法,在两个子类中分别实现
3.将Bank类作成单例
Java第七天 2012年5月13日
一、复习
static
属性 类变量 全类共有 类加载时初始化,类名访问
方法 静态方法 类名调用,静态方法中不能访问类的非静态成员,可以覆盖,只能被静态方法覆盖,没有多态
初始代码块 类加载时运行
类加载:
一个类编译之后会形成.class文件,储存了类的全部信息。
当JVM第一次使用一个类的时候,会根据ClassPath找到对应的.class文件,用输入流把文件中的信息读入JVM并保存起来,这样,JVM就“认识”了这个类
类加载时机:第一次用这个类的时候
1. 第一次创建类的对象,会加载
2. 访问类的静态成员,会加载
3. 声明类的引用,不会加载
4. 加载子类,必然先加载父类
5. 如果调用的是子类从父类中继承下来的静态方法,只会加载父类
6. 如果访问的是类的静态常量,如果在编译的时候能够确定这个常量的值,则运行时不会加载,
否则,编译时无法确定常量值,那么运行时就会加载
设计模式:单例模式
用途:一个类只能有一个对象
实现:会有一个静态的属性,就是该类的一个对象。提供一个静态方法,来获得这个唯一的静态属性(单例),同时构造方法私有
final
变量:常量,一旦赋值,不能改变,为属性常量赋值的时机:对于实例常量,初始化或者构造方法中可以赋值;
对于类常量(static final),初始化或者静态初始代码块中可以赋值
方法:不能被覆盖
类 :不能被继承 所有方法默认都是final的
abstract
方法 :只有定义,没有实现
类 :不能构造对象,但可以用来声明一个引用(作为编译时类型)
如果一个类有抽象方法,这个类必须是抽象类,如果一个类是抽象类,不一定有抽象方法
子类继承一个抽象类,就必须覆盖(实现)父类(抽象类)中的所有抽象方法,否则,子类就也得是抽象类
抽象类有构造方法,给子类继承
通过abstract,我们可以把一个方法的定义放在父类,留给子类实现。
二、接口
接口是一种程序结构,是特殊的抽象类。
接口中的方法必须都是公开的抽象方法(public abstract),接口中的属性都是公开静态常量(public static final)。
接口中没有构造方法
abstract class ClassA{
public static final int A = 10;
public static final double B = 3.14;
public abstract void m1();
public abstract void m2();
}
interface IA{
int A = 10;
double B = 3.14;
void m1();
void m2();
} //与上面的抽象类逻辑上等价,编译之后,也会生成一个IA.class文件
所以一个源文件中可以写多个接口,但只能有一个公开接口,并且要与源文件的名字一致
接口可以继承,但是只能由接口继承。
在用类去继承时要换用 implements 关键字,这时类和接口也不叫做继承关系,而是实现关系,但其实质也是继承。
一个类可以继承也只能继承另外一个类,但是可以实现多个接口,其语法是在implements后面写接口名,多个接口以“,”分隔。
接口之间是可以多继承的,其语法和类的继承语法是相同的。
在接口多继承时,在extends后写接口名如果要继承多个接口,接口名以“,”分隔,接口的继承关系只是把其父接口中的抽象方法继承到子接口中。
要实现接口就必须实现接口中的所有方法。
一个类可以在继承一个类的同时,还可以实现一个或多个接口。采用接口就绕开了单继承限制。
class Impl extends ClassE implements IA,IB{……}
接口类型也可以做为编译时类型使用,但其实际的运行时类型必须是完全实现接口的类的对象实例,这样就使多态变得很灵活了
接口类型的引用指向接口某一个实现类的对象
接口的意义:
1,接口可以实现多继承。
2,用接口可以实现混合类型(主类型,副类型),java中可以通过接口分出主次类型。主类型使用继承,副类型,使用接口实现。
3,接口进一步深化了标准的思想,接口本身就是一个标准,
他起到了降低耦合性的作用,接口可以使方法的定义和实现相分离,也就是将接口的定义者和实现者相分离,
接口也可以用于降低模块间或系统间的耦合性。
针对接口编程可以屏蔽不同实现间的差异,看到的只是实现好的功能。
练习:
在原有的雇员练习上修改代码:
公司会给SalariedEmployee每月另外发送2000元加班费
给BasePlueSalesEmployee发放1000元加班费
改写原有的代码,加入以上的逻辑
并写一个方法,打印出本月公司总共发放了多少加班费
练习:
验证歌德巴赫猜想,输入一个大于6的偶数,请输出这个偶数能被分解为哪两个质数的和
如 10=3+7 12=5+7
质数:除了1和自身,不能被任何数整除的数
要求:两个人一组合作完成,一个人负责拆数,另一个人负责写方法,判断一个数是不是质数
接口:定义标准,
接口的实现:实现标准
接口的调用者:标准的使用
针对接口编程原则,也就是按照标准实现。
接口的定义者定义好了标准,接口的使用者就可以写使用代码,接口的实现者写好实现之后把实现对象传入接口的使用者中。
他调用接口中方法也就是掉用接口实现中的方法。
这种过程叫做接口的回调(先有标准使用者,后有标准实现者,把标准的实现者对象传给使用者,再由使用者通过接口调用标准实现者的方法)。
作业:
1、体验接口回调
根据文档和给定的class文件,实现接口,并且将实现类的对象作为参数调用Match类中的match方法,实现石头剪刀布的游戏
2、接口
为SavingAccount和CreditAccount各自添加一个子类
LoanSavingAccount类:用户可以贷款,不可以透支
LoanCreditAccount类:用户可以贷款,可以透支
说明:贷款和透支是不一样的,透支指的是账户余额小于0,而贷款用户需要一个贷款额的属性.
在ATM机上,用户可以选择贷款,也可以选择还贷款,而还贷款就是要把账户余额上的资金转到贷款额上
例如:用户余额10000元,贷款额100000元,用户可以选择还款5000元,则用户余额变为5000,贷款额变为95000元.
利用接口来抽象出LoanSavingAccount类和LoanCreditAccount类的共性
接口中的方法:
requestLoan:贷款
payLoan:还贷
getLoan:获取用户贷款总额
为Bank类添加三个方法,
贷款:参数 id,贷款额,返回修改过的Account对象
还贷款:参数 id,还款额,返回修改过的Account对象
统计所有账户贷款的总数