1、Java中的值传递
值传递意味着对于传给方法的每个参数都会制作一份副本然后将副本而不是原始值传递给方法并且通过参数的名进行引用。
注意:虽然传值机制对于所有参数类型都适用,但是他对对象类型的作用与最基本类型变量的作用并不相同。对于对象类型参数传递,是将一份指向这个对象的引用的副本而不是对象本身传递给方法,因为这个引用的副本仍然指向同一个对象。因此将方法参数设置为final可以避免传递给方法的对象的引用被意外修改,但这无法避免修改对象本身。
2、使用初始化代码块
静态初始化代码块使用static关键字定义代码块,并且当类加载时执行一次。
1 class InitialCodeBlock { 2 static int [] values = new int [10]; 3 4 //initial block 5 static { 6 for(int i=0;i++ ;i < 10){ 7 values[i] = (int)(100.0*Math.random()); 8 } 9 } 10 }
3、垃圾回收(garbage colecction)
垃圾回收在Java中自动进行,但这并不意味着这些对象会直接从内存中消失,对象在程序中变得不可访问之后,他还需要一段时间才会消失这不会直接影响程序的运行,仅仅意味着不能信任已销毁对象所占的内存会被立即释放。对于大多数情况并没有什么影响,唯一有影响的情形是涉及到对象非常大(几百万字节)、或是需要不断的创建和销毁对象的情形,在这种情况下如果遇到问题,可以试图调用System中的静态方法gc()鼓励Java JVM执行垃圾回收并且释放这些对象所占的内存,这对于java JVM 来说是尽力而为的任务。当gc()方法返回时,JVM会试图回收已舍弃的对象占据的空间但是不能保证全部恢复,也有可能因为调用gc()方法使情况变得更糟。如果垃圾回收正在进行一些恢复内存的准备,调用gc()方法会取消这种准备,进而导致进度变慢。
4、对类打包
package Geometry; public class Line { }
public关键词让类在包外部也能够访问,如果去掉public关键词,那么自由Geometry包中的类能够访问。
打包后的代码:
package testpackage.aaa; public class bbb { public static void main (String arg []){ System.out.println("test package!"); } }
编译:
注意:命令的运行目录应该是源文件的目录,但是classpath的目录却是“package testpackage.aaa;”最外层目录的上一层目录。
运行:
注意:运行的时候是要考虑包的目录结构的。
5、根据约定java中类名以大写字母开始,名为ClassName的类必须存储在名为ClassName.java 的文件中。而且所有在包PackageName中的类的文件名必须包含在名为PackageName的目录下。一般的可以使用任意多的点号隔开的名称来定义包,但是包名必须能够反映用来存储这个包的目录结构。
6、运行包含包层次的class时必须回到和包层次一致的最外层目录然后执行,例如:java testpackage.aaa.bbb (当前工作目录应该在testpackage的上一层目录),把已经编译好的class文件归档:
F:javaProject>jar cvf testpackage.jar testpackageaaa*.class
已添加清单
正在添加: testpackage/aaa/bbb.class(输入 = 429) (输出 = 294)(压缩了 31%)
7、java中的装箱和拆箱
装箱:从基本类型到对应的类类型的转换称为装箱转换,而自动进行的转换被称为自动装箱。
拆箱:将指向对象的引用转化成对象封装的值,编译器会插入拆箱转换。编译器通过为对象插入一个指向xxxValue()方法的调用来实现这个目的。
8、类成员的访问控制
1)无访问属性:允许同一个包中任意类的方法访问。
2)public:只要申明为public,就允许任位置的类方法的访问。
3)private:只允许类内部的方法访问。
4)protected:允许同一个包中任意类的方法访问并且允许任意位置的子类访问。
9、关于setXXX()的思考
使用方法来修改private的数据成员似乎有点奇怪,因为只需要将这个数据成员变成public就可以达到相同的使用目的。在这种情况下,使用的方法的优点是可以对需要设置的新值有效性检测并阻止赋给不适当的值。当然如果不想允许private成员的值被修改,就不要在类中包含赋值方法。
10、嵌套类
1)非静态嵌套类
public class Outside { //Nested class public class Inside{ //Details of Inside class } //More members of Outside class }
创建Outside类型对象之前不能创建Inside对象,但是当申明一个包含嵌套类型的类对象时,并不一定要创建嵌套对象-----除非这个包围类的构造函数已经创建他们。
例如:
Outside outer = new Outside();//这里并没有创建嵌套类Inside的对象,如果要创建嵌套类的对象,就必须使用包围类的名称最为限定名来引用嵌套类。
Outside.Inside inner = outer.new Inside();//在包含类外穿件嵌套类,完全限定名是必须的。
Inside inner = new Inside();//在嵌套类Outside对象内部创建Inside嵌套类
2)静态嵌套类
public class Outside { //Nested class public static class Inside{ //Details of Inside class } //More members of Outside class }
通过把Inside申明为静态的,就可以独立于任何Outside类对象申明嵌套类对象了,也不用管是否创建了Outside对象了。例如:
Outside.Inside example = new Outside.Inside ();
注意:静态嵌套类能拥有静态成员和非静态成员,但是非静态嵌套类只能拥有非静态成员。
11、扩展类与继承
1)类继承
图6-2是继承后的访问权限,与包之间的访问权限是不相同的。如果一个类要能在包外部被访问,就必须定义为public的访问属性。如下图5-11所示:
2)派生类对象
说明:基类中只有public和protected能被子类继承,没有标识属性、private还有构造函数都是不能被继承的。
a)派生类的构造函数
在派生类中调用基类构造函数如下:
super(Parameter ...);//super("dog")
b)覆盖基类方法
在派生类中定义方法名一样且参数列表完全相同的方法,才能够覆盖基类中原有的方法。在派生类中方法的访问属性可以与基类中的一样或者限制更少一些,但是不能限制更多。例如:在基类中声明一个方法为public,那么在派生类中将不能省略属性,也不能将这个方法设定为private或protected。
c)@Override标记
在派生类中视图定义一个方法覆盖子类中的方法时,很容易因为派生类方法的签名(访问属性、参数列表)犯错,如果派生类中方法的名称和参数列表与超类方法不同,那么将是对基类方法进行重载而不是覆盖。@Override标记试图防止发生这类错误。
d)选择基类的访问属性
①组成类的外部接口应该声明为public,通常不应该将数据成员设置为public,除非是一般用途的常量。
②将自己的类作为基类,应该讲数据成员设置为private,并且在必要时提供public方法来访问和操作他们。
③当拥有一个类包并希望无限制的访问同一包中的任何类的数据成员时,可以使用protected。
④省略成员的数据属性会导致类成员对包中其他类可用,同时会阻止不在同一包中的子类继承---当在另一包中查看时,效果与private一样。
12、多态
说明:polymorphism意味着呈现多种不同形式或形状的能力,编程中意味着某个给定类型的单个变量能被引用不同类型的对象,并且能够自动调用这个变量引用的对象的特定方法。这允许单个方法能根据每次调用的应用的不同对象类型来产生不同的行为,如上图6-4所示。
1)多态满足的条件
①多态对派生类对象起作用,可以将对派生类的引用存储到派生类类型的变量中,但是也可以存储到任意直接或间接基类类型的变量中。而且指向派生类对象的引用必须存储到直接或间接基类的类型的变量中来实现多态。
②多态意味着由方法调用中所涉及的对象的实际类型来决定调用的方法,而不是由存储引用的变量的类型来决定。
③在派生类中,这些方法的定义都必须与在基类中的一样,并且必须都有一个不会含有更多限制的访问修饰符。
④对于多态的行为,派生类中的方法的返回类型必须与基类中对应的方法返回类型一样或是基类返回类型的子类。如果返回类型不同,但是派生类中方法返回的类型是基类返回类型的子类,那么返回类型将被认为是协变的(covariant)。如下代码:
public class Animal{ Animal createCreature(){ //code to create an Animal and return a reference to it } } public class Dog extends Animal { @Override Dog createCreature(){ //Code to create a Dog object and return a reference to it } }
2)多态时需要满足的条件
①派生类对象的方法必须通过基类的类型变量进行调用。
②调用方法必须在派生类中重写。
③调用的方法还必须声明为基类成员。
④基类和派生类中的方法签名必须一样。
⑤要么基类和派生类中方法的返回值一样,要么返回值必须是协变的。
⑥派生类中方法的访问修饰不能比基类中有更多的限制。
小结:①因为基类变量能够引用任意派生类对象,所以引用的对象类型直到程序执行时才知道。因此,必须在程序运行时动态选择要执行的方法---在程序编译时无法决定。
②多态只运用于实例方法,不能运用于数据成员。
使用多态:
源文件目录结构:
编译:分别编译三个包,最后编译运行TryPolymorphism,
F:javaProjectAnimalPolymorphismAnimal>javac -classpath "F:javaProjectAnimal
Polymorphism" *.java
F:javaProjectAnimalPolymorphismDog>javac -classpath "F:javaProjectAnimalPol
ymorphism" *.java
F:javaProjectAnimalPolymorphismCat>javac -classpath "F:javaProjectAnimalPol
ymorphism" *.java
F:javaProjectAnimalPolymorphism>javac TryPolymorphism.java
运行:
Animal包中的Animal类:
package Animal; public class Animal { public Animal (String name){ this.name = name; } @Override public String toString(){ return "Animal is " + getName() + ","; } public String sound(){ return ""; } public String getName(){ return this.name; } private String name; }
Dog包中的Dog类:
package Dog ; import Animal.Animal; public class Dog extends Animal { public Dog(String dogName){ super("Dog"); this.dogName = dogName; } @Override public String toString (){ return "Dog name is " + getDogName() + "."; } @Override public String sound(){ return "wang wang ..."; } public String getDogName(){ return this.dogName ; } private String dogName; }
Cat包中的Cat类:
package Cat ; import Animal.Animal; public class Cat extends Animal { public Cat(String catName){ super("cat"); this.catName = catName; } @Override public String toString (){ return "Cat name is " + getCatName() + "."; } @Override public String sound(){ return "Miao Miao ..."; } public String getCatName(){ return this.catName ; } private String catName; }
main函数所在类:
import Animal.Animal; import Dog.Dog; import Cat.Cat; import java.util.Random; import java.lang.Thread; public class TryPolymorphism { public static void main (String [] arg ){ Animal animals [] = { new Dog("XiaoHei"), new Cat("MiMi") }; Animal petChoice; Random select = new Random (); for(int i = 0; i < 5; i++ ){ petChoice = animals[select.nextInt(animals.length)]; System.out.println(petChoice); System.out.println(petChoice.sound()+" "); try{ Thread.sleep(1000); }catch(Exception e){ } } } }
通过以下代码判断对象的类型:
for(int i = 0; i < 5; i++ ){ petChoice = animals[select.nextInt(animals.length)]; Class objectType = petChoice.getClass(); System.out.println(objectType.getName()); System.out.println(petChoice); System.out.println(petChoice.sound()+" "); try{ Thread.sleep(1000); }catch(Exception e){ } }
注意:获取到的是完全限定名,也就是包括包名,如果处在默认包中则没有包名。
Class对象:程序中的每个数组类型和每个基本类型都对应着一个Class对象可以使用getClass()获取特定类或特定接口类型对应的Class对象,或者更直接的在任何类、接口、或基本类型后加.class就会获得Class对象的引用。每个Class对象都是唯一的,所以可以通过此方法来精准的判断对象的类型。如下代码:
for(int i = 0; i < 5; i++ ){ petChoice = animals[select.nextInt(animals.length)]; Class objectType = petChoice.getClass(); System.out.println(objectType.getName()); if(petChoice.getClass() == Dog.class){ System.out.println("It's Dog object!"); } System.out.println(petChoice); System.out.println(petChoice.sound()+" "); try{ Thread.sleep(1000); }catch(Exception e){ } }
13、抽象类
在上面多态代码中Animal基类中的sound()方法是为实现多态用来被子类覆盖的,因此在基类中实现该方法被没有什么意义。因此java中针对这种情况设计了抽象类(abstract class)。抽象类是指一种类,其中声明一个或多个方法,但是没有定义它们这些方法的方法体被省略掉。
例如:将Animal类修改为抽象类
package Animal; public class abstract Animal { public Animal (String name){ this.name = name; } @Override public String toString(){ return "Animal is " + getName() + ","; } public abstract String sound(); public String getName(){ return this.name; } private String name; }
注意:①abstract方法不能是private的因为private方法不能被子类继承进而不能不能被子类重写。抽象类是不能被实例化的,但是可声明对应的变量。例:Animal thePet = null;
②从一个抽象基类派生出一个类时,并不需要实现基类中的所有抽象方法。这种情况下,该子类也是抽象,也不能实例化。并且也应该加上abstract关键词,只要子类中有没有实现的基类中的抽象方法,哪怕是一个,子类都不能实例化,并且一定要加上abstract关键词。直到所有基类中的抽象方法全部被实现了,子类才能实例化,才能不加abstract关键词。
14、超类
所有类都是以Object作为基础的,所以Object是每个类的超类。
利用从Object继承过来的方法clone()复制对象,clone()可以拷贝当前对象的一个副本,两个对象类型相同,并且有相同的值域。如果原始对象含有引用类型的域,那么只会拷贝该对象的引用,也就是拷贝前后两个对象的引用类型的域指向同一个对象,任意一个域的引用改变该对象该对象就被改变了。