java基础之 sun认证之3
来源:互联网 发布:js中给input赋值 编辑:程序博客网 时间:2024/04/30 06:59
第八章:高级语言特性
静态变量 static
一个类只有一个静态变量,跟对象没有关系。被类的所有实例共享;如果子类没有覆盖,也共享父类的静态成员。
一般直接使用类名来访问 “类名.静态变量名”。可以在没有任何实例时调用。
在某种意义上类似于全局变量(Java里没有全局变量,这只是C和C++的说法)
不能在 static 方法或代码块里访问非 static 成员(变量或方法)
能继承和覆盖,但覆盖 static 方法必须也是 static 的方法。
1.可以修饰属性、方法、初始代码块,成为类变量、静态方法、静态初始化代码块。
注:初始代码块是在类中而不是在任何方法之内的代码块。
2.类变量、静态方法、静态初始化代码块与具体的某个对象无关,只与类相关,是全类公有的。 在类加载时初始化。
3.类加载:JVM通过CLASSPATH找到字节码文件,并将字节码文件中的内容通过I/O流读到JVM并保存的过程
在虚拟机的生命周期中一个类只被加载一次。
注:Java命令的作用是启动JVM (Java Virtual Mechine)。
4.tatic 定义的是一块为整个类共有的一块存储区域,其发生变化时访问到的数据都是经过变化的。
5.为什么主方法必须是静态的?
主方法是整个应用程序的入口,JVM只能通过类名去调用主方法。
6.类变量和静态方法可以在没有对象的情况下用:类名.方法名(或属性名)来访问。
7.静态方法不可被覆盖(允许在子类中定义同名的静态方法,但是没有多态)
父类如果是静态方法,子类不能覆盖为非静态方法。父类如果是非静态方法,子类不能覆盖为静态方法。
争论:静态方法可以覆盖但是没有多态。
思考:没有多态的覆盖叫覆盖吗?
在静态方法中不允许调用本类中的非静态成员。
8.静态初始化代码块只在类加载的时候运行一次,以再也不执行了。所以静态代码块一般被用来初始化静态成员。
9.不加static为动态初始化代码块,在创建对象时被调用(在构造函数之前)。
10.最后要注意的一点就是 static 不能修饰局部变量。
什么时候类加载
第一次需要使用类信息时加载。
类加载的原则:延迟加载,能不加载就不加载。
触发类加载的几种情况:
(1)、调用静态成员时,会加载静态成员真正所在的类及其父类。
通过子类调用父类的静态成员时,只会加载父类而不会加载子类。
(2)、第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。
(3)、加载子类会先加载父类。
注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。
例:public static final int a =123;
但是如果上面的等式右值改成表达式(且该表达式在编译时不能确定其值)时则会加载类。
例:public static final int a = math.PI
如果访问的是类的公开静态常量,那么如果编译器在编译的时候能确定这个常量的值,就不会被加载;
如果编译时不能确定其值的话,则运行时加载
类加载的顺序:
1.加载静态成员/代码块:
先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员。
同一个类里的静态成员/代码块,按写代码的顺序加载。
如果其间调用静态方法,则调用时会先运行静态方法,再继续加载。同一个类里调用静态方法时,可以不理会写代码的顺序。
调用父类的静态成员,可以像调用自己的一样;但调用其子类的静态成员,必须使用“子类名.成员名”来调用。
2.加载非静态成员/代码块:
先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。
同一个类里的非静态成员/代码块,按写代码的顺序加载。同一个类里调用方法时,可以不理会写代码的顺序。
但调用属性时,必须注意加载顺序。一般编译不通过,如果能在加载前调用,值为默认初始值(如:null 或者 0)。
调用父类的非静态成员(private 除外),也可以像调用自己的一样。
3.调用构造方法:
先递归地调用父类的构造方法(Object的最先);默认调用父类空参的,也可在第一行写明调用父类某个带参的。
再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。
注意:如果加载时遇到 override 的成员,可看作是所需创建的类型赋值给当前类型。
其调用按多态用法:只有非静态方法有多态;而静态方法、静态属性、非静态属性都没有多态。
假设子类override父类的所有成员,包括静态成员、非静态属性和非静态方法。
由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;
由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。
成员,包括变量和方法
成员变量,包括实例变量和静态变量
final 修饰符
(最终的、最后的)当final修饰时,不能被改变,不能被继承
1.final 可以用来修饰类、属性和方法、局部变量。
2.final 修饰一个属性时,该属性成为常量。
(1)对于在构造方法中利用final进行赋值时,此时在构造之前系统设置的默认值相对于构造方法失效。
(2)对于实例常量的赋值有两次机会
在初始化的时候通过声明赋值
在构造的时候(构造方法里)赋值
注:不能在声明时赋值一次,在构造时再赋值一次。
注意:当final修饰实例变量时,实例变量不会自动初始化为0;但必须给他赋值才能通过编译。
3.final 修饰方法时,该方法成为一个不可覆盖的方法。这样可以保持方法的稳定性。
如果一个方法前有修饰词private或static,则系统会自动在前面加上final。
即 private 和 static 方法默认均为 final 方法。
4.final 常常和 static、public 配合来修饰一个实例变量,表示为一个全类公有的公开静态常量。
例: public static final int a = 33;
在这种情况下属性虽然公开了,但由于是一个静态常量所以并不算破坏类的封装。
5.final 修饰类时,此类不可被继承,即final类没有子类。
一个 final 类中的所有方法默认全是final方法。
final 不能修饰构造方法,构造方法不能被继承更谈不上被子类方法覆盖。
关于 final 的设计模式:不变模式
1、不变模式:一个对象一旦产生就不可能再修改( string 就是典型的不变模式);
通过不变模式可以做到对象共享;
2、池化思想:用一个存储区域来存放一些公用资源以减少存储空间的开销。
有池的类型:boolean,byte,int,short,long,char,(池范围在-127~128之间)
(float,double 等小数没有池)
例:在String类中有个串池(在代码区)。
池:堆里的一片独立空间。目的是拿空间换时间,让运算效率更高。
(1)如果用Stirng str = “abc” 来创建一个对象时,则系统会先在“串池”中寻找有没有“abc”这个字符串
如果有则直接将对象指向串池中对应的地址,如果没有则在串池中创建一个“abc”字符串。
所以:String str1 = “abc”;
String str2 = “abc”;
Str1 == str2 返回值是ture;他们的地址是一样的。
也就是说str1和str2都指向了代码空间中相同的一个地址,而这个地址空间保存就是是字符串"abc"
字符串是不可改变的类型,所以可以共享。所以串池里不会有相同的两个字符串。
(2)如果用String str = new String("abc")则直接开辟一块内存放"abc"这个字符串。
所以上面这语句,创建两个"abc",一个在池,一个是对象
String str2 = new String("abc");
Str == str2 返回值是false;他们的地址是不一样的。
即是说str和str2分别指向了堆空间中不同的两个地址,而这两个地址空间保存的都是字符串"abc"
StringBuffer 类(java.lang下的)。
对于字符串连接
String str=”1”+”2”+”3”+”4”;
产生:
12 123 1234
这在串池中产生多余对象,而我们真正需要的只有最后一个对象,这种方式在时间和空间上都造成相当大的浪费。
所以我们应该使用 StringBuffer(线程安全的) 或者 StringBuilder(线程不安全的)来解决
解决方案:
StringBuffer sb = new StringBuffer(“1”);
Sb.append(“2”);
Sb.append(“3”);
Sb.append(“4”);
S = sb.toString();
解决后的方案比解决前在运行的时间上快2个数量级。
StringBuilder (1.5版本后出现的)
线程不安全的,在多线程并发时会出现问题。但仍是字符串合并的首选。
运行效率比 StringBuffer 快一倍。
abstract 抽象
1.可用来修饰类、方法
2.abstract 修饰类时,则该类成为一个抽象类。
抽象类不可生成对象(但可以有构造方法留给子类使用),必须被继承使用。
抽象类可以声明,作为编译时类型,但不能作为运行时类型。
abstract 永远不会和 private,static,final 同时出现。( 因为抽象必须被继承。)
3.abstract 修饰方法时,则该方法成为一个抽象方法,抽象方法不能有实现;由子类覆盖后实现。
比较:private void print(){};表示方法的空实现
abstract void print();表示方法为抽象方法,没有实现
4.抽象方法从某中意义上来说是制定了一个标准,父类并不实现,留给子类去实现。
注:抽象类中不一定要有抽象方法,但有抽象方法的类一定是抽象类。
抽象类可以有抽象方法和非抽象方法。实现抽象类的第一个具体类必须实现其所有抽象方法。
5.关于抽象类的设计模式:模板方法
灵活性和不变性
interface 接口
1、定义:接口不是类,而是一组对类需求的描述,这些类要遵从接口描述的统一格式进行定义。
定义一个接口用关键字 interface。
例:public interface a{……}
2、接口是一种特殊的抽象类。
在一个接口中,所有的方法为公开、抽象的方法,所有的属性都是公开、静态、常量。
所以接口中的所有属性可省略修饰符:public static final。也只能用这三个修饰符。
接口中所有的方法可省略修饰符:public abstract。但这些都是默认存在的。
3、一个类实现一个接口必须实现接口中所有的方法,否则其为一抽象类。而且实现类的方法需要 public
实现接口用关键字 implements.
所谓实现一个接口就是实现接口中所有的方法。
例:class Aimple implements A{……..};
4、一个类除了继承另一个类外(且只能继承一个类),还可以实现多个接口(接口之间用逗号分割)。
接口可以实现变相的多继承。
例:class Aimple extends Arrylist implements A,B,C{…}
5、不能用“new 接口名”来实例化一个接口,但可以声明一个接口。
6、接口与接口之间可以多继承。
例:interface face1 extends face2,face3{}
接口的继承相当于接口的合并
7、接口的作用
(1)、间接实现多继承。
用接口来实现多继承并不会增加类关系的复杂度。因为接口不是类,是在类的基础上的再次抽象。
父类:主类型 接口:副类型
典例:父亲(主) 和 老师(副)
(2)、允许我们为一个类定义出混合类型。
(3)、通过接口制定标准
接 口:标准的定义 定义标准
接口的调用者:标准的使用 使用标准
接口的实现类:标准的实现 实现标准
接口的回调:先有接口的使用者,再有接口的实现者,最后把接口的实现者的对象传到接口的使用者中,
并由接口的使用者通过接口来调用接口实现者的方法。
例:sun公司提供一套访问数据库的接口(标准),java程序员访问数据库时针对数据库接口编程。
接口由各个数据库厂商负责实现。
(4)、解耦合作用:采用接口可以最大限度的做到弱耦合,将标准的实现者与标准的制定者隔离
(例:我们通过JDBC接口来屏蔽底层数据库的差异)
8、接口的编程设计原则
(1)、尽量针对接口编程(能用接口就尽量用接口)
(2)、接口隔离原则(用若干个小接口取代一个大接口)
这样可以只暴露想暴露的方法,实现一个更高层次的封装。
9、注意点:
(1)、一个文件只能有一个 public 接口,且与文件名相同。
(2)、在一个文件中不能同时定义一个 public 接口和一个 public 类。
(3)、接口与实体类之间只有实现关系,没有继承关系;
抽象类与类之间只有继承关系没有实现关系。接口与接口之间只有继承关系,且允许多继承。
(4)、接口中可以不写 public,但在子类实现接口的过程中 public 不可省略。
接口 VS 抽象类
1、接口中不能有具体的实现,但抽象类可以。
2、一个类要实现一个接口必须实现其里面所有的方法,而抽象类不必。
3、通过接口可以实现多继承,而抽象类做不到。
4、接口不能有构造方法,而抽象类可以。
5、实体类与接口之间只有实现关系,而实体类与抽象类只有继承关系
抽象类与接口之间既有实现关系又有继承关系。
6、接口中的方法默认都是公开抽象方法,属性默认都是公开静态常量,而抽象类不是。
修饰符的使用情况:
(Y表可用;不写表示不可用)
修饰符 类 属性 方法 局部变量(所有局部变量都不能用修饰符)
public Y Y Y
protected Y Y
(default) Y Y Y
private Y Y
static Y Y
final Y Y Y Y
abstract Y Y
访问权限控制:
修饰符 同一个类 同一个包 (不同包)子类 其他包
public Y Y Y Y
protected Y Y Y
(default) Y Y
private Y
Object类
1、object类是类层次结构的根类,他是所有类默认的父类。
2、object类中的其中三个方法。
(1)、finalize()
当一个对象被垃圾收集的时候,最后会由JVM调用这个对象的finalize方法;
注意:这个方法一般不用,也不能将释放资源的代码放在这个方法里;只有调用C程序时,才可能要用到
(2)、toString()
存放对象地址的哈希码值。
返回一个对象的字符串表示形式。打印一个对象其实就是打印这个对象toString方法的返回值。
可以覆盖类的toString()方法,从而打印我们需要的数据。 Public String toString(){……}
(3)、equals(Object obj)
用来判断对象的值是否相等。前提是覆盖了equals方法。Object类中的equals方法判断的依然是地址
注意:String类已经覆盖了equals方法,所以能用equals来判断String对象的值是否相等。
下面是覆盖equals方法的标准流程:
/*************************************************************************/
public boolean equals(Object obj){
//第一步:现判断两个对象地址是否相等
if(this == obj) return true;
//第二步:如果参数是null的话直接返回false;
if(obj == null) return false;
//第三步:如果两个对象不是同一个类型直接返回false
if (getClass() != obj.getClass()) return false;
//?? if(!(this.getClass.getName().equals(o.getClass.getName())) return false;
//第四步:将待比较对象强转成指定类型,然后自定义比较规则
Student s = (Student) obj;
if(s.name.equals(this.name)&&s.age==this.age) return true;
else return false;
}
/*************************************************************************/
覆盖equals的原则: 1.自反性(自己=自己)、 2.对称性(y=x则x=y)、
3.一致性(多次调用,结果一致)、 4.传递性(A=B,B=C则A=C)。
非空原则: t1.equals(Null)返回False;(如果t1不等于空)
(4)、clone 克隆,拷贝
一个对象参与序列化过程,那么对象流会记录该对象的状态,当再次序列化时,
会重复序列化前面记录的对象初始状态,我们可以用对象克隆技术来解决这个问题
1 类中覆盖父类的clone方法,提升protected为public
2 类实现Cloneable接口
浅拷贝:只简单复制对象的属性
深拷贝:如果被复制对象的属性也是一个对象,则还会复制这个属性对象
这种复制是一个递归的过程,通过对象序列化实现
《Java5.0新特性》
四大点(枚举、泛型、注释、..);5 小点(包装类、静态应用、可变长参数、for-each、..)
一、自动装箱 和 自动解箱技术
装箱Autoboxing,也翻译作 封箱;解箱Unautoboxing(也译作 解封)
1、自动装箱技术:编译器会自动将简单类型转换成封装类型。
2、编译器会自动将封装类型转换成简单类型
3、注意:自动装箱和自动解箱只会在必要的情况下执行。
int 能隐式提升成 long;但Integer不能隐式提升成Long,只能提升成Number
封装之后就成类,只能由子类转成父类;而Integer和Long是Number的不同子类。
如: int i; short s; Integer II; Short SS;
可以 i=SS; 但不可以 II=s; //赋值时,右边的数先转成左边数的对应类型,再进行隐式类型提升
二、静态引用概念:
用 import static 节省以后的书写。
引入静态属性 import static java.lang.System.out;
引入静态方法 import static java.lang.Math.random;
import static 只能引入静态的方法或属性;不能只引入类或非静态的方法。
如:import static java.lang.System.*;
out.println(“a”); //等于System.out.println("a"); 由于out是一个字段,所以不能更节省了
如果 import static java.lang.System.gc; 则可以直接在程序中用 gc(); //等于System.gc();
三、可变长参数
一个方法的参数列表中最多只能有一个可变长参数,而且这个变长参数必须是最后一个参数
方法调用时只在必要时去匹配变长参数。
/**********变长参数的例子*************************************/
import static java.lang.System.*;//节省书写,System.out直接写out
public class TestVararg {
public static void main(String... args){
m();
m("Liucy");
m("Liucy","Hiloo");
}
static void m(String... s){out.println("m(String...)");}
//s可以看作是一个字符串数组String[] s
static void m(){out.println("m()");}
static void m(String s){out.println("m(String)");}
} //m(String... s) 是最后匹配的
/**********************************************************/
四、枚举 enum
1、定义:枚举是一个具有特定值的类型,对用户来说只能任取其一。
对于面向对象来说时一个类的对象已经创建好,用户不能新生枚举对象,只能选择一个已经生成的对象。
2、枚举本质上也是一个类。枚举值之间用逗号分开,以分号结束(如果后面没有其他语句,分号可不写)。
3、枚举分为两种:类型安全的枚举模式和类型不安全的枚举模式
4、枚举的超类(父类)是:Java.lang.Enum。枚举是 final 类所以不能继承或被继承。但可以实现接口。
枚举中可以写构造方法,但构造方法必需是私有的,而且默认也是 私有的 private
5、一个枚举值实际上是一个公开静态的常量,也是这个类的一个对象。
6、枚举中可以定义抽象方法,但实现在各个枚举值中(匿名内部类的方式隐含继承)
由于枚举默认是 final 型,不能被继承,所以不能直接用抽象方法(抽象方法必须被继承)
在枚举中定义抽象方法后,需要在自己的每个枚举值中实现抽象方法。
枚举是编译期语法,编译后生成类型安全的普通类
values()静态方法,返回枚举的元素数组
name方法
/**********************************************************/
final class Season1{ //用 final 不让人继承
private Season1(){} //用 private 构造方法,不让人 new 出来
public static final Season1 SPRING=new Season1("春");
public static final Season1 SUMMER=new Season1("夏");
public static final Season1 AUTUMN=new Season1("秋");
public static final Season1 WINTER=new Season1("冬");
String name; //将"春夏秋冬"设为本类型,而不是24种基本类型,为防止值被更改
private Season1(String name){
this.name=name;
}
public String getName(){
return this.name;
}}
/********上面是以前版本时自定义的枚举,下面是新版的枚举写法********/
enum Season2{
SPRING("春"), SUMMER("夏"), AUTUMN("秋"), WINTER("冬");
String name;
Season2(String name){ this.name=name; }
public String getName(){return this.name;}
}//注意:枚举类是有序的;如:Season2.SPRING.ordinal()
/**********************************************************/
/*******关于枚举的例子****************************************/
import static java.lang.System.*;
public class TestTeacher {
public static void main(String[] args) {
for(TarenaTeacher t:TarenaTeacher.values()){
t.teach();
}}}
enum TarenaTeacher{
LIUCY("liuchunyang"){void teach(){out.println(name+" teach UC");}},
CHENZQ("chenzongquan"){void teach(){out.println(name+" teach C++");}},
HAIGE("wanghaige"){void teach(){out.println(name+" teach OOAD");}};
String name;
TarenaTeacher(String name){this.name=name;}
abstract void teach();
}
/**********************************************************/
enum Animals {
DOG ("WangWang") , CAT("meow") , FISH("burble");
String sound;
Animals ( String s ) { sound = s; }
}
class TestEnum {
static Animals a;
public static void main ( String[] args ) {
System.out.println ( a.DOG.sound + " " + a.FISH.sound );
}}
/**********************************************************/
五、新型 for 循环 for—each,用于追求数组与集合的遍历方式统一
1、数组举例:
int[] ss = {1,2,3,4,5,6};
for(int i=0; i<ss.length; i++){
System.out.print(ss[i]);
} //以上是以前的 for 循环遍历,比较下面的for—each
System.out.println();
for(int i : ss){
System.out.print(i);
2、集合举例:
List ll = new ArrayList();
for(Object o : ll ){
System.out.println(o);
}
注:凡是实现了java.lang.Iterable接口的类就能用 for—each遍历
用 for—each时,不能用list.remove()删除,因为他内部的迭代器无法调用,造成多线程出错。
这时只能用 for 配合迭代器使用。
六、泛型 Generic
1、为了解决类型安全的集合问题引入了泛型。
泛型是编译检查时的依据,也是编译期语法。
(编译期语法:编译期有效,编译后擦除,不存在于运行期)
2、简单的范型应用:集合(ArrayList, Set, Map, Iterator, Comparable)
List<String> l = new ArrayList<String>();
<String>:表示该集合中只能存放String类型对象。
3、使用了泛型技术的集合在编译时会有类型检查,不再需要强制类型转换。
String str = l.get(2); //因为List<String> l, 所以 Error
注:一个集合所允许的类型就是这个泛型的类型或这个泛型的子类型。
4、List<Number> l = new ArrayList<Integer> //Error
List<Integer> l = new ArrayList<Integer> //Right
必须类型一致,泛型没有多态
5、泛型的通配符<?>
泛型的通配符表示该集合可以存放任意类型的对象。但只有访问,不可以修改。
static void print( Cllection<?> c ){
for( Object o : c )
out.println(o);
}
6、带范围的泛型通配符
泛型的声明约定T表示类型,E表示元素
(1)、上界通配符,向下匹配:<? extends Number> 表明“extends”或“implements”,认为是 final 的
表示该集合元素可以为Number类型及其子类型(包括接口),例如 Number,Integer,Double
此时集合可以进行访问但不能修改。即不允许调用此对象的add,set等方法;但可以使用 for-each 或 get.
(2)、下界通配符,向上匹配:<? super Number>
表示该集合元素可以为Number类型及其父类型,直至 Object。
可以使用 for-each,add,addAll,set,get等方法
(3)、接口实现:<? extends Comparable>
表示该集合元素可以为实现了Comparable接口的类
7、泛型方法
在返回类型与修饰符之间可以定义一个泛型方法,令后面的泛型统一
这里只能用 extends 定义,不能用 super ;后面可以跟类(但只能有一个,且要放在首位)其余是接口
符号只有 & //“&”表示“与”;逗号表示后面的另一部分
静态方法里面,不能使用类定义的泛型,只能用自己定义的;因为静态方法可以直接调用;
所以普通方法可以使用类定义的及自己定义的泛型
public static <T> void copy(T[] array,Stack<T> sta){……}
public static <T,E extends T> void copy (T[] array,Stack<E> sta){…..}
public static <T extends Number&Comparable> void copy(List<T> list,T[] t);
8、不能使用泛型的情况:
(1)、带泛型的类不能成为 Throwable 类和 Exception 类的子类
因为cathc()中不能出现泛型。
(2)、不能用泛型来 new 一个对象
如:T t = new T();
(3)、静态方法不能使用类的泛型,因为静态方法中没有对象的概念。
9、在使用接口的时候指明泛型。
class Student implements Comparable<Student>{…….}
10、泛型类
/********************************************************************/
class MyClass<T>{
public void m1(T t){}
public T m2(){
return null;
}}
/********************************************************************/
第九章:
内部类(nested classes) (非重点)
1.定义:定义在其他类中的类,叫内部类(内置类)。内部类是一种编译时的语法,编译后生成
的两个类是独立的两个类。内部类配合接口使用,来强制做到弱耦合(局部内部类,或私有成员内部类)。
2.内部类存在的意义在于可以自由的访问外部类的任何成员(包括私有成员),但外部类不能直接访问内部类的
成员。所有使用内部类的地方都可以不使用内部类;使用内部类可以使程序更加的简洁(但牺牲可读性),
便于命名规范和划分层次结构。
3.内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖。
4.内部类可用 static,protected 和 private 修饰。(而外部类只能使用 public 和 default)。
5.内部类的分类:成员内部类、局部内部类、静态内部类、匿名内部类。
(注意:前三种内部类与变量类似,可以对照参考变量)
① 成员内部类(实例内部类):作为外部类的一个成员存在,与外部类的属性、方法并列。
成员内部类可看作外部类的实例变量。
在内部类中访问实例变量:this.属性
在内部类访问外部类的实例变量:外部类名.this.属性。
对于一个名为outer 的外部类和其内部定义的名为inner 的内部类。
编译完成后出现outer.class 和outer$inner.class 两类。
不可以有静态属性和方法(final 的除外),因为 static 在加载类的时候创建,这时内部类还没被创建
如果在外部类的外部访问内部类,使用out.inner.***
建立内部类对象时应注意:
在创建成员内部类的实例时,外部类的实例必须存在:
在外部类的内部可以直接使用inner s=new inner(); 因为外部类知道 inner 是哪个类。
而在外部类的外部,要生成一个内部类对象,需要通过外部类对象生成。
Outer.Inner in = new Outer().new Inner();
相当于:Outer out = new Outer(); Outer.Inner in = out.new Inner();
错误的定义方式:Outer.Inner in=new Outer.Inner()。
② 局部内部类:在方法中定义的内部类称为局部内部类。
类似局部变量,不可加修饰符 public、protected 和 private,其范围为定义它的代码块。
可以访问外部类的所有成员,此外,还可以访问所在方法中的 final 类型的参数和变量。
在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。
要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。
局部内部类不能声明接口和枚举。
③ 静态内部类:(也叫嵌套类)
静态内部类定义在类中,在任何方法外,用 static 定义。
静态内部类能直接访问外部类的静态成员;
不能直接访问外部类的实例成员,但可通过外部类的实例(new 对象)来访问。
静态内部类里面可以定义静态成员(其他内部类不可以)。
生成(new)一个静态内部类不需要外部类成员,这是静态内部类和成员内部类的区别。
静态内部类的对象可以直接生成: Outer.Inner in=new Outer.Inner();
对比成员内部类:Outer.Inner in = Outer.new Inner();
而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。
静态内部类不可用 private 来进行定义。例子:
对于两个类,拥有相同的方法:
/*************************************************************************/
/*class People{void run();}
interface Machine{void run();}
此时有一个robot类:
class Robot extends People implement Machine.
此时run()不可直接实现。*/
interface Machine{ void run();}
class Person{ void run(){System.out.println("run");}}
class Robot extends Person{
private class MachineHeart implements Machine{
public void run(){System.out.println("heart run");}
}
public void run(){System.out.println("Robot run");}
Machine getMachine(){return new MachineHeart();}
}
class Test{
public static void main(String[] args){
Robot robot=new Robot();
Machine m=robot.getMachine();
m.run();
robot.run();
}}
/*************************************************************************/
注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
这是唯一一种必须使用内部类的情况。
用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
④ 匿名内部类:
【1】匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
【2】不同的是他是用一种隐含的方式实现一个接口或继承一个类,而且他只需要一个对象
【3】在继承这个类时,根本就没有打算添加任何方法。
【4】匿名内部类大部分情况都是为了实现接口的回调。
注:匿名内部类一定是在 new 的后面
其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。
因其为局部内部类,那么局部内部类的所有限制都对其生效。
匿名内部类是唯一一种无构造方法类。
注:这是因为构造器的名字必须和类名相同,而匿名内部类没有类名。
匿名内部类在编译的时候由系统自动起名Out$1.class。
因匿名内部类无构造方法,所以其使用范围非常的有限。
结尾需加上分号。
匿名内部类的例子:
/*************************************************************************/
public class test{
public static void main(String[] args){
B.print(new A(){
public void getConnection(){ System.out.println("Connection....");}
});
}}
interface A{ void getConnection();}
class B{
public static void print(A a){ a.getConnection();}
}
/*************************************************************************/
枚举和接口可以在类的内部定义,但不能在方法内部定义。
接口里面还可以定义多重接口和类。
类放在什么位置,就相当于什么成员。
内部类的用途:
封装类型:把标准公开,把标准的实现者作为内部类隐藏起来,
强制要求使用者通过标准访问标准的实现者,从而强制做到弱耦合!
直接访问外部类的成员
配合接口,实现多继承,当父类和接口方法定义发生冲突的时候,就必须借助内部类来区分
模板回调
从内部类继承:
由于直接构造实例内部类时,JVM会自动使内部类实例引用它的外部类实例。
但如果下面Sample类通过以下方式构造对象时:Sample s = new Sample();
JVM无法决定Sample实例引用哪个Outer实例,为了避免这种错误的发生,在编译阶段,java编译器会要求Sample类的构造方法必须通过参数传递一个Outer实例的引用,然后在构造方法中调用super语句来建立Sample实例与Outer实例的关联关系。
/*************************************************************************/
public class Sample extends Outer.Inner{
//public Sample(){} //编译错误
public Sample(Outer o){ o.super(); }
public static void main(String args[]){
Outer outer1=new Outer(1);
Outer outer2=new Outer(2);
Outer.Inner in=outer1.new Inner();
in.print();
Sample s1=new Sample(outer1);
Sample s2=new Sample(outer2);
s1.print(); //打印a=1
s2.print(); //打印a=2
}}
/*************************************************************************/
内部接口:
在一个类中也可以定义内部接口
在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。
在接口中还可以定义接口,这种接口默认也是public static 的,如Map.Entry就是这种接口
静态变量 static
一个类只有一个静态变量,跟对象没有关系。被类的所有实例共享;如果子类没有覆盖,也共享父类的静态成员。
一般直接使用类名来访问 “类名.静态变量名”。可以在没有任何实例时调用。
在某种意义上类似于全局变量(Java里没有全局变量,这只是C和C++的说法)
不能在 static 方法或代码块里访问非 static 成员(变量或方法)
能继承和覆盖,但覆盖 static 方法必须也是 static 的方法。
1.可以修饰属性、方法、初始代码块,成为类变量、静态方法、静态初始化代码块。
注:初始代码块是在类中而不是在任何方法之内的代码块。
2.类变量、静态方法、静态初始化代码块与具体的某个对象无关,只与类相关,是全类公有的。 在类加载时初始化。
3.类加载:JVM通过CLASSPATH找到字节码文件,并将字节码文件中的内容通过I/O流读到JVM并保存的过程
在虚拟机的生命周期中一个类只被加载一次。
注:Java命令的作用是启动JVM (Java Virtual Mechine)。
4.tatic 定义的是一块为整个类共有的一块存储区域,其发生变化时访问到的数据都是经过变化的。
5.为什么主方法必须是静态的?
主方法是整个应用程序的入口,JVM只能通过类名去调用主方法。
6.类变量和静态方法可以在没有对象的情况下用:类名.方法名(或属性名)来访问。
7.静态方法不可被覆盖(允许在子类中定义同名的静态方法,但是没有多态)
父类如果是静态方法,子类不能覆盖为非静态方法。父类如果是非静态方法,子类不能覆盖为静态方法。
争论:静态方法可以覆盖但是没有多态。
思考:没有多态的覆盖叫覆盖吗?
在静态方法中不允许调用本类中的非静态成员。
8.静态初始化代码块只在类加载的时候运行一次,以再也不执行了。所以静态代码块一般被用来初始化静态成员。
9.不加static为动态初始化代码块,在创建对象时被调用(在构造函数之前)。
10.最后要注意的一点就是 static 不能修饰局部变量。
什么时候类加载
第一次需要使用类信息时加载。
类加载的原则:延迟加载,能不加载就不加载。
触发类加载的几种情况:
(1)、调用静态成员时,会加载静态成员真正所在的类及其父类。
通过子类调用父类的静态成员时,只会加载父类而不会加载子类。
(2)、第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。
(3)、加载子类会先加载父类。
注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。
例:public static final int a =123;
但是如果上面的等式右值改成表达式(且该表达式在编译时不能确定其值)时则会加载类。
例:public static final int a = math.PI
如果访问的是类的公开静态常量,那么如果编译器在编译的时候能确定这个常量的值,就不会被加载;
如果编译时不能确定其值的话,则运行时加载
类加载的顺序:
1.加载静态成员/代码块:
先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员。
同一个类里的静态成员/代码块,按写代码的顺序加载。
如果其间调用静态方法,则调用时会先运行静态方法,再继续加载。同一个类里调用静态方法时,可以不理会写代码的顺序。
调用父类的静态成员,可以像调用自己的一样;但调用其子类的静态成员,必须使用“子类名.成员名”来调用。
2.加载非静态成员/代码块:
先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。
同一个类里的非静态成员/代码块,按写代码的顺序加载。同一个类里调用方法时,可以不理会写代码的顺序。
但调用属性时,必须注意加载顺序。一般编译不通过,如果能在加载前调用,值为默认初始值(如:null 或者 0)。
调用父类的非静态成员(private 除外),也可以像调用自己的一样。
3.调用构造方法:
先递归地调用父类的构造方法(Object的最先);默认调用父类空参的,也可在第一行写明调用父类某个带参的。
再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。
注意:如果加载时遇到 override 的成员,可看作是所需创建的类型赋值给当前类型。
其调用按多态用法:只有非静态方法有多态;而静态方法、静态属性、非静态属性都没有多态。
假设子类override父类的所有成员,包括静态成员、非静态属性和非静态方法。
由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;
由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。
成员,包括变量和方法
成员变量,包括实例变量和静态变量
final 修饰符
(最终的、最后的)当final修饰时,不能被改变,不能被继承
1.final 可以用来修饰类、属性和方法、局部变量。
2.final 修饰一个属性时,该属性成为常量。
(1)对于在构造方法中利用final进行赋值时,此时在构造之前系统设置的默认值相对于构造方法失效。
(2)对于实例常量的赋值有两次机会
在初始化的时候通过声明赋值
在构造的时候(构造方法里)赋值
注:不能在声明时赋值一次,在构造时再赋值一次。
注意:当final修饰实例变量时,实例变量不会自动初始化为0;但必须给他赋值才能通过编译。
3.final 修饰方法时,该方法成为一个不可覆盖的方法。这样可以保持方法的稳定性。
如果一个方法前有修饰词private或static,则系统会自动在前面加上final。
即 private 和 static 方法默认均为 final 方法。
4.final 常常和 static、public 配合来修饰一个实例变量,表示为一个全类公有的公开静态常量。
例: public static final int a = 33;
在这种情况下属性虽然公开了,但由于是一个静态常量所以并不算破坏类的封装。
5.final 修饰类时,此类不可被继承,即final类没有子类。
一个 final 类中的所有方法默认全是final方法。
final 不能修饰构造方法,构造方法不能被继承更谈不上被子类方法覆盖。
关于 final 的设计模式:不变模式
1、不变模式:一个对象一旦产生就不可能再修改( string 就是典型的不变模式);
通过不变模式可以做到对象共享;
2、池化思想:用一个存储区域来存放一些公用资源以减少存储空间的开销。
有池的类型:boolean,byte,int,short,long,char,(池范围在-127~128之间)
(float,double 等小数没有池)
例:在String类中有个串池(在代码区)。
池:堆里的一片独立空间。目的是拿空间换时间,让运算效率更高。
(1)如果用Stirng str = “abc” 来创建一个对象时,则系统会先在“串池”中寻找有没有“abc”这个字符串
如果有则直接将对象指向串池中对应的地址,如果没有则在串池中创建一个“abc”字符串。
所以:String str1 = “abc”;
String str2 = “abc”;
Str1 == str2 返回值是ture;他们的地址是一样的。
也就是说str1和str2都指向了代码空间中相同的一个地址,而这个地址空间保存就是是字符串"abc"
字符串是不可改变的类型,所以可以共享。所以串池里不会有相同的两个字符串。
(2)如果用String str = new String("abc")则直接开辟一块内存放"abc"这个字符串。
所以上面这语句,创建两个"abc",一个在池,一个是对象
String str2 = new String("abc");
Str == str2 返回值是false;他们的地址是不一样的。
即是说str和str2分别指向了堆空间中不同的两个地址,而这两个地址空间保存的都是字符串"abc"
StringBuffer 类(java.lang下的)。
对于字符串连接
String str=”1”+”2”+”3”+”4”;
产生:
12 123 1234
这在串池中产生多余对象,而我们真正需要的只有最后一个对象,这种方式在时间和空间上都造成相当大的浪费。
所以我们应该使用 StringBuffer(线程安全的) 或者 StringBuilder(线程不安全的)来解决
解决方案:
StringBuffer sb = new StringBuffer(“1”);
Sb.append(“2”);
Sb.append(“3”);
Sb.append(“4”);
S = sb.toString();
解决后的方案比解决前在运行的时间上快2个数量级。
StringBuilder (1.5版本后出现的)
线程不安全的,在多线程并发时会出现问题。但仍是字符串合并的首选。
运行效率比 StringBuffer 快一倍。
abstract 抽象
1.可用来修饰类、方法
2.abstract 修饰类时,则该类成为一个抽象类。
抽象类不可生成对象(但可以有构造方法留给子类使用),必须被继承使用。
抽象类可以声明,作为编译时类型,但不能作为运行时类型。
abstract 永远不会和 private,static,final 同时出现。( 因为抽象必须被继承。)
3.abstract 修饰方法时,则该方法成为一个抽象方法,抽象方法不能有实现;由子类覆盖后实现。
比较:private void print(){};表示方法的空实现
abstract void print();表示方法为抽象方法,没有实现
4.抽象方法从某中意义上来说是制定了一个标准,父类并不实现,留给子类去实现。
注:抽象类中不一定要有抽象方法,但有抽象方法的类一定是抽象类。
抽象类可以有抽象方法和非抽象方法。实现抽象类的第一个具体类必须实现其所有抽象方法。
5.关于抽象类的设计模式:模板方法
灵活性和不变性
interface 接口
1、定义:接口不是类,而是一组对类需求的描述,这些类要遵从接口描述的统一格式进行定义。
定义一个接口用关键字 interface。
例:public interface a{……}
2、接口是一种特殊的抽象类。
在一个接口中,所有的方法为公开、抽象的方法,所有的属性都是公开、静态、常量。
所以接口中的所有属性可省略修饰符:public static final。也只能用这三个修饰符。
接口中所有的方法可省略修饰符:public abstract。但这些都是默认存在的。
3、一个类实现一个接口必须实现接口中所有的方法,否则其为一抽象类。而且实现类的方法需要 public
实现接口用关键字 implements.
所谓实现一个接口就是实现接口中所有的方法。
例:class Aimple implements A{……..};
4、一个类除了继承另一个类外(且只能继承一个类),还可以实现多个接口(接口之间用逗号分割)。
接口可以实现变相的多继承。
例:class Aimple extends Arrylist implements A,B,C{…}
5、不能用“new 接口名”来实例化一个接口,但可以声明一个接口。
6、接口与接口之间可以多继承。
例:interface face1 extends face2,face3{}
接口的继承相当于接口的合并
7、接口的作用
(1)、间接实现多继承。
用接口来实现多继承并不会增加类关系的复杂度。因为接口不是类,是在类的基础上的再次抽象。
父类:主类型 接口:副类型
典例:父亲(主) 和 老师(副)
(2)、允许我们为一个类定义出混合类型。
(3)、通过接口制定标准
接 口:标准的定义 定义标准
接口的调用者:标准的使用 使用标准
接口的实现类:标准的实现 实现标准
接口的回调:先有接口的使用者,再有接口的实现者,最后把接口的实现者的对象传到接口的使用者中,
并由接口的使用者通过接口来调用接口实现者的方法。
例:sun公司提供一套访问数据库的接口(标准),java程序员访问数据库时针对数据库接口编程。
接口由各个数据库厂商负责实现。
(4)、解耦合作用:采用接口可以最大限度的做到弱耦合,将标准的实现者与标准的制定者隔离
(例:我们通过JDBC接口来屏蔽底层数据库的差异)
8、接口的编程设计原则
(1)、尽量针对接口编程(能用接口就尽量用接口)
(2)、接口隔离原则(用若干个小接口取代一个大接口)
这样可以只暴露想暴露的方法,实现一个更高层次的封装。
9、注意点:
(1)、一个文件只能有一个 public 接口,且与文件名相同。
(2)、在一个文件中不能同时定义一个 public 接口和一个 public 类。
(3)、接口与实体类之间只有实现关系,没有继承关系;
抽象类与类之间只有继承关系没有实现关系。接口与接口之间只有继承关系,且允许多继承。
(4)、接口中可以不写 public,但在子类实现接口的过程中 public 不可省略。
接口 VS 抽象类
1、接口中不能有具体的实现,但抽象类可以。
2、一个类要实现一个接口必须实现其里面所有的方法,而抽象类不必。
3、通过接口可以实现多继承,而抽象类做不到。
4、接口不能有构造方法,而抽象类可以。
5、实体类与接口之间只有实现关系,而实体类与抽象类只有继承关系
抽象类与接口之间既有实现关系又有继承关系。
6、接口中的方法默认都是公开抽象方法,属性默认都是公开静态常量,而抽象类不是。
修饰符的使用情况:
(Y表可用;不写表示不可用)
修饰符 类 属性 方法 局部变量(所有局部变量都不能用修饰符)
public Y Y Y
protected Y Y
(default) Y Y Y
private Y Y
static Y Y
final Y Y Y Y
abstract Y Y
访问权限控制:
修饰符 同一个类 同一个包 (不同包)子类 其他包
public Y Y Y Y
protected Y Y Y
(default) Y Y
private Y
Object类
1、object类是类层次结构的根类,他是所有类默认的父类。
2、object类中的其中三个方法。
(1)、finalize()
当一个对象被垃圾收集的时候,最后会由JVM调用这个对象的finalize方法;
注意:这个方法一般不用,也不能将释放资源的代码放在这个方法里;只有调用C程序时,才可能要用到
(2)、toString()
存放对象地址的哈希码值。
返回一个对象的字符串表示形式。打印一个对象其实就是打印这个对象toString方法的返回值。
可以覆盖类的toString()方法,从而打印我们需要的数据。 Public String toString(){……}
(3)、equals(Object obj)
用来判断对象的值是否相等。前提是覆盖了equals方法。Object类中的equals方法判断的依然是地址
注意:String类已经覆盖了equals方法,所以能用equals来判断String对象的值是否相等。
下面是覆盖equals方法的标准流程:
/*************************************************************************/
public boolean equals(Object obj){
//第一步:现判断两个对象地址是否相等
if(this == obj) return true;
//第二步:如果参数是null的话直接返回false;
if(obj == null) return false;
//第三步:如果两个对象不是同一个类型直接返回false
if (getClass() != obj.getClass()) return false;
//?? if(!(this.getClass.getName().equals(o.getClass.getName())) return false;
//第四步:将待比较对象强转成指定类型,然后自定义比较规则
Student s = (Student) obj;
if(s.name.equals(this.name)&&s.age==this.age) return true;
else return false;
}
/*************************************************************************/
覆盖equals的原则: 1.自反性(自己=自己)、 2.对称性(y=x则x=y)、
3.一致性(多次调用,结果一致)、 4.传递性(A=B,B=C则A=C)。
非空原则: t1.equals(Null)返回False;(如果t1不等于空)
(4)、clone 克隆,拷贝
一个对象参与序列化过程,那么对象流会记录该对象的状态,当再次序列化时,
会重复序列化前面记录的对象初始状态,我们可以用对象克隆技术来解决这个问题
1 类中覆盖父类的clone方法,提升protected为public
2 类实现Cloneable接口
浅拷贝:只简单复制对象的属性
深拷贝:如果被复制对象的属性也是一个对象,则还会复制这个属性对象
这种复制是一个递归的过程,通过对象序列化实现
《Java5.0新特性》
四大点(枚举、泛型、注释、..);5 小点(包装类、静态应用、可变长参数、for-each、..)
一、自动装箱 和 自动解箱技术
装箱Autoboxing,也翻译作 封箱;解箱Unautoboxing(也译作 解封)
1、自动装箱技术:编译器会自动将简单类型转换成封装类型。
2、编译器会自动将封装类型转换成简单类型
3、注意:自动装箱和自动解箱只会在必要的情况下执行。
int 能隐式提升成 long;但Integer不能隐式提升成Long,只能提升成Number
封装之后就成类,只能由子类转成父类;而Integer和Long是Number的不同子类。
如: int i; short s; Integer II; Short SS;
可以 i=SS; 但不可以 II=s; //赋值时,右边的数先转成左边数的对应类型,再进行隐式类型提升
二、静态引用概念:
用 import static 节省以后的书写。
引入静态属性 import static java.lang.System.out;
引入静态方法 import static java.lang.Math.random;
import static 只能引入静态的方法或属性;不能只引入类或非静态的方法。
如:import static java.lang.System.*;
out.println(“a”); //等于System.out.println("a"); 由于out是一个字段,所以不能更节省了
如果 import static java.lang.System.gc; 则可以直接在程序中用 gc(); //等于System.gc();
三、可变长参数
一个方法的参数列表中最多只能有一个可变长参数,而且这个变长参数必须是最后一个参数
方法调用时只在必要时去匹配变长参数。
/**********变长参数的例子*************************************/
import static java.lang.System.*;//节省书写,System.out直接写out
public class TestVararg {
public static void main(String... args){
m();
m("Liucy");
m("Liucy","Hiloo");
}
static void m(String... s){out.println("m(String...)");}
//s可以看作是一个字符串数组String[] s
static void m(){out.println("m()");}
static void m(String s){out.println("m(String)");}
} //m(String... s) 是最后匹配的
/**********************************************************/
四、枚举 enum
1、定义:枚举是一个具有特定值的类型,对用户来说只能任取其一。
对于面向对象来说时一个类的对象已经创建好,用户不能新生枚举对象,只能选择一个已经生成的对象。
2、枚举本质上也是一个类。枚举值之间用逗号分开,以分号结束(如果后面没有其他语句,分号可不写)。
3、枚举分为两种:类型安全的枚举模式和类型不安全的枚举模式
4、枚举的超类(父类)是:Java.lang.Enum。枚举是 final 类所以不能继承或被继承。但可以实现接口。
枚举中可以写构造方法,但构造方法必需是私有的,而且默认也是 私有的 private
5、一个枚举值实际上是一个公开静态的常量,也是这个类的一个对象。
6、枚举中可以定义抽象方法,但实现在各个枚举值中(匿名内部类的方式隐含继承)
由于枚举默认是 final 型,不能被继承,所以不能直接用抽象方法(抽象方法必须被继承)
在枚举中定义抽象方法后,需要在自己的每个枚举值中实现抽象方法。
枚举是编译期语法,编译后生成类型安全的普通类
values()静态方法,返回枚举的元素数组
name方法
/**********************************************************/
final class Season1{ //用 final 不让人继承
private Season1(){} //用 private 构造方法,不让人 new 出来
public static final Season1 SPRING=new Season1("春");
public static final Season1 SUMMER=new Season1("夏");
public static final Season1 AUTUMN=new Season1("秋");
public static final Season1 WINTER=new Season1("冬");
String name; //将"春夏秋冬"设为本类型,而不是24种基本类型,为防止值被更改
private Season1(String name){
this.name=name;
}
public String getName(){
return this.name;
}}
/********上面是以前版本时自定义的枚举,下面是新版的枚举写法********/
enum Season2{
SPRING("春"), SUMMER("夏"), AUTUMN("秋"), WINTER("冬");
String name;
Season2(String name){ this.name=name; }
public String getName(){return this.name;}
}//注意:枚举类是有序的;如:Season2.SPRING.ordinal()
/**********************************************************/
/*******关于枚举的例子****************************************/
import static java.lang.System.*;
public class TestTeacher {
public static void main(String[] args) {
for(TarenaTeacher t:TarenaTeacher.values()){
t.teach();
}}}
enum TarenaTeacher{
LIUCY("liuchunyang"){void teach(){out.println(name+" teach UC");}},
CHENZQ("chenzongquan"){void teach(){out.println(name+" teach C++");}},
HAIGE("wanghaige"){void teach(){out.println(name+" teach OOAD");}};
String name;
TarenaTeacher(String name){this.name=name;}
abstract void teach();
}
/**********************************************************/
enum Animals {
DOG ("WangWang") , CAT("meow") , FISH("burble");
String sound;
Animals ( String s ) { sound = s; }
}
class TestEnum {
static Animals a;
public static void main ( String[] args ) {
System.out.println ( a.DOG.sound + " " + a.FISH.sound );
}}
/**********************************************************/
五、新型 for 循环 for—each,用于追求数组与集合的遍历方式统一
1、数组举例:
int[] ss = {1,2,3,4,5,6};
for(int i=0; i<ss.length; i++){
System.out.print(ss[i]);
} //以上是以前的 for 循环遍历,比较下面的for—each
System.out.println();
for(int i : ss){
System.out.print(i);
2、集合举例:
List ll = new ArrayList();
for(Object o : ll ){
System.out.println(o);
}
注:凡是实现了java.lang.Iterable接口的类就能用 for—each遍历
用 for—each时,不能用list.remove()删除,因为他内部的迭代器无法调用,造成多线程出错。
这时只能用 for 配合迭代器使用。
六、泛型 Generic
1、为了解决类型安全的集合问题引入了泛型。
泛型是编译检查时的依据,也是编译期语法。
(编译期语法:编译期有效,编译后擦除,不存在于运行期)
2、简单的范型应用:集合(ArrayList, Set, Map, Iterator, Comparable)
List<String> l = new ArrayList<String>();
<String>:表示该集合中只能存放String类型对象。
3、使用了泛型技术的集合在编译时会有类型检查,不再需要强制类型转换。
String str = l.get(2); //因为List<String> l, 所以 Error
注:一个集合所允许的类型就是这个泛型的类型或这个泛型的子类型。
4、List<Number> l = new ArrayList<Integer> //Error
List<Integer> l = new ArrayList<Integer> //Right
必须类型一致,泛型没有多态
5、泛型的通配符<?>
泛型的通配符表示该集合可以存放任意类型的对象。但只有访问,不可以修改。
static void print( Cllection<?> c ){
for( Object o : c )
out.println(o);
}
6、带范围的泛型通配符
泛型的声明约定T表示类型,E表示元素
(1)、上界通配符,向下匹配:<? extends Number> 表明“extends”或“implements”,认为是 final 的
表示该集合元素可以为Number类型及其子类型(包括接口),例如 Number,Integer,Double
此时集合可以进行访问但不能修改。即不允许调用此对象的add,set等方法;但可以使用 for-each 或 get.
(2)、下界通配符,向上匹配:<? super Number>
表示该集合元素可以为Number类型及其父类型,直至 Object。
可以使用 for-each,add,addAll,set,get等方法
(3)、接口实现:<? extends Comparable>
表示该集合元素可以为实现了Comparable接口的类
7、泛型方法
在返回类型与修饰符之间可以定义一个泛型方法,令后面的泛型统一
这里只能用 extends 定义,不能用 super ;后面可以跟类(但只能有一个,且要放在首位)其余是接口
符号只有 & //“&”表示“与”;逗号表示后面的另一部分
静态方法里面,不能使用类定义的泛型,只能用自己定义的;因为静态方法可以直接调用;
所以普通方法可以使用类定义的及自己定义的泛型
public static <T> void copy(T[] array,Stack<T> sta){……}
public static <T,E extends T> void copy (T[] array,Stack<E> sta){…..}
public static <T extends Number&Comparable> void copy(List<T> list,T[] t);
8、不能使用泛型的情况:
(1)、带泛型的类不能成为 Throwable 类和 Exception 类的子类
因为cathc()中不能出现泛型。
(2)、不能用泛型来 new 一个对象
如:T t = new T();
(3)、静态方法不能使用类的泛型,因为静态方法中没有对象的概念。
9、在使用接口的时候指明泛型。
class Student implements Comparable<Student>{…….}
10、泛型类
/********************************************************************/
class MyClass<T>{
public void m1(T t){}
public T m2(){
return null;
}}
/********************************************************************/
第九章:
内部类(nested classes) (非重点)
1.定义:定义在其他类中的类,叫内部类(内置类)。内部类是一种编译时的语法,编译后生成
的两个类是独立的两个类。内部类配合接口使用,来强制做到弱耦合(局部内部类,或私有成员内部类)。
2.内部类存在的意义在于可以自由的访问外部类的任何成员(包括私有成员),但外部类不能直接访问内部类的
成员。所有使用内部类的地方都可以不使用内部类;使用内部类可以使程序更加的简洁(但牺牲可读性),
便于命名规范和划分层次结构。
3.内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖。
4.内部类可用 static,protected 和 private 修饰。(而外部类只能使用 public 和 default)。
5.内部类的分类:成员内部类、局部内部类、静态内部类、匿名内部类。
(注意:前三种内部类与变量类似,可以对照参考变量)
① 成员内部类(实例内部类):作为外部类的一个成员存在,与外部类的属性、方法并列。
成员内部类可看作外部类的实例变量。
在内部类中访问实例变量:this.属性
在内部类访问外部类的实例变量:外部类名.this.属性。
对于一个名为outer 的外部类和其内部定义的名为inner 的内部类。
编译完成后出现outer.class 和outer$inner.class 两类。
不可以有静态属性和方法(final 的除外),因为 static 在加载类的时候创建,这时内部类还没被创建
如果在外部类的外部访问内部类,使用out.inner.***
建立内部类对象时应注意:
在创建成员内部类的实例时,外部类的实例必须存在:
在外部类的内部可以直接使用inner s=new inner(); 因为外部类知道 inner 是哪个类。
而在外部类的外部,要生成一个内部类对象,需要通过外部类对象生成。
Outer.Inner in = new Outer().new Inner();
相当于:Outer out = new Outer(); Outer.Inner in = out.new Inner();
错误的定义方式:Outer.Inner in=new Outer.Inner()。
② 局部内部类:在方法中定义的内部类称为局部内部类。
类似局部变量,不可加修饰符 public、protected 和 private,其范围为定义它的代码块。
可以访问外部类的所有成员,此外,还可以访问所在方法中的 final 类型的参数和变量。
在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。
要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。
局部内部类不能声明接口和枚举。
③ 静态内部类:(也叫嵌套类)
静态内部类定义在类中,在任何方法外,用 static 定义。
静态内部类能直接访问外部类的静态成员;
不能直接访问外部类的实例成员,但可通过外部类的实例(new 对象)来访问。
静态内部类里面可以定义静态成员(其他内部类不可以)。
生成(new)一个静态内部类不需要外部类成员,这是静态内部类和成员内部类的区别。
静态内部类的对象可以直接生成: Outer.Inner in=new Outer.Inner();
对比成员内部类:Outer.Inner in = Outer.new Inner();
而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。
静态内部类不可用 private 来进行定义。例子:
对于两个类,拥有相同的方法:
/*************************************************************************/
/*class People{void run();}
interface Machine{void run();}
此时有一个robot类:
class Robot extends People implement Machine.
此时run()不可直接实现。*/
interface Machine{ void run();}
class Person{ void run(){System.out.println("run");}}
class Robot extends Person{
private class MachineHeart implements Machine{
public void run(){System.out.println("heart run");}
}
public void run(){System.out.println("Robot run");}
Machine getMachine(){return new MachineHeart();}
}
class Test{
public static void main(String[] args){
Robot robot=new Robot();
Machine m=robot.getMachine();
m.run();
robot.run();
}}
/*************************************************************************/
注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
这是唯一一种必须使用内部类的情况。
用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
④ 匿名内部类:
【1】匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
【2】不同的是他是用一种隐含的方式实现一个接口或继承一个类,而且他只需要一个对象
【3】在继承这个类时,根本就没有打算添加任何方法。
【4】匿名内部类大部分情况都是为了实现接口的回调。
注:匿名内部类一定是在 new 的后面
其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。
因其为局部内部类,那么局部内部类的所有限制都对其生效。
匿名内部类是唯一一种无构造方法类。
注:这是因为构造器的名字必须和类名相同,而匿名内部类没有类名。
匿名内部类在编译的时候由系统自动起名Out$1.class。
因匿名内部类无构造方法,所以其使用范围非常的有限。
结尾需加上分号。
匿名内部类的例子:
/*************************************************************************/
public class test{
public static void main(String[] args){
B.print(new A(){
public void getConnection(){ System.out.println("Connection....");}
});
}}
interface A{ void getConnection();}
class B{
public static void print(A a){ a.getConnection();}
}
/*************************************************************************/
枚举和接口可以在类的内部定义,但不能在方法内部定义。
接口里面还可以定义多重接口和类。
类放在什么位置,就相当于什么成员。
内部类的用途:
封装类型:把标准公开,把标准的实现者作为内部类隐藏起来,
强制要求使用者通过标准访问标准的实现者,从而强制做到弱耦合!
直接访问外部类的成员
配合接口,实现多继承,当父类和接口方法定义发生冲突的时候,就必须借助内部类来区分
模板回调
从内部类继承:
由于直接构造实例内部类时,JVM会自动使内部类实例引用它的外部类实例。
但如果下面Sample类通过以下方式构造对象时:Sample s = new Sample();
JVM无法决定Sample实例引用哪个Outer实例,为了避免这种错误的发生,在编译阶段,java编译器会要求Sample类的构造方法必须通过参数传递一个Outer实例的引用,然后在构造方法中调用super语句来建立Sample实例与Outer实例的关联关系。
/*************************************************************************/
public class Sample extends Outer.Inner{
//public Sample(){} //编译错误
public Sample(Outer o){ o.super(); }
public static void main(String args[]){
Outer outer1=new Outer(1);
Outer outer2=new Outer(2);
Outer.Inner in=outer1.new Inner();
in.print();
Sample s1=new Sample(outer1);
Sample s2=new Sample(outer2);
s1.print(); //打印a=1
s2.print(); //打印a=2
}}
/*************************************************************************/
内部接口:
在一个类中也可以定义内部接口
在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。
在接口中还可以定义接口,这种接口默认也是public static 的,如Map.Entry就是这种接口
- java基础之 sun认证之3
- java基础之 sun认证之二
- java基础之 sun认证之一
- SUN公司SCJP认证学习之旅
- 网络安全基础之认证
- Sun认证Java课程
- Sun JAVA 认证介绍
- Sun JAVA 认证介绍
- SUN JAVA认证介绍
- Java认证之总结
- SUN 的Java认证体系
- Sun Java系列认证介绍
- sun 公司的java 认证
- Java程序员认证考试 ---SUN认证
- Java认证之精辟总结
- Java认证之精辟总结
- Java认证之精辟总结
- SUN认证Java2程序员考试(SCJP)试题解析-Java基础-Java-编程开发
- C语言第二堂作业
- 咀嚼几个无线术语
- java基础之 sun认证之二
- C++程序编译过程简介
- 批量导入添加。(struts2)
- java基础之 sun认证之3
- 排序算法-基数排序
- 上海奥运冠军获免费医保 刘翔终身免费看病
- java基础之 集合
- ibatis+spring+struts整合应用实例
- 记录下js库网址
- java基础之 异常
- 一个收废品的大哥毁了我的三观
- java基础之 反射