package Function;
/*
* void方法不需要return语句,但它能用于终止方法并返回到方法的调用者 提前返回。
* 有无return语句都会返回到调用函数,非void的函数会捎带一个返回数而已。
*/
/* class Function {
public static void printGrade(double score){
if(score < 0 || score > 100){
System.out.println("Invalid score");
return; //提前返回到调用函数
}
...
* ...
}
}*/
/*
* double[] myList = new double[10];
* 先声明一个数组,并不分配空间
* 创建数组,并分配空间
* myList指向该数组 myList只是这个数组的引用
*
* 增强for循环遍历:对myList中的每个元素u进行以下操作,但是不能进行修改数组中元素或者改变遍历顺序
* for(double u : myList){
* System.out.println(u);
* }
*
* list2 = list1; list1的引用值复制给list2 此时list2指向了list1 两个数组的内容相同,但并没有复制数组
*
* 给方法传递参数时:如果参数类型是基本类型 则pass-by-value;如果参数类型是数组类型,则pass-by-sharing,即传递的是数组的引用。
* public class Test{
public static void main(String[] args) {
int x = 1;
int[] y = new int[10];
System.out.println("y[0] is:"+y[0]);
m(x,y);
System.out.println("x is:"+x);
System.out.println("y[0] is:"+y[0]);
}
public static void m(int number,int[] array){ //相当于 int[] array = y
number = 2;
array[0] = 3;
}
}
*/
/*
*既可以向方法传递一个数组 也可以从方法返回一个数组
*/
/* public class Test{
public static int[] reverse(int[] list) { //注意函数的类型为 int[]
int[] result = new int[list.length];
for(int i = 0, j = result.length - 1;i < list.length;i++,j--)
result[i] = list[j];
return result; //返回值类型为 int[]
}
}
*/
//统计一个字符数组中每个字母出现的次数 字符数组 char[] ch = new char[100]或者 char[] ch = {'a','b','c'};
/* for(int i = 0;i < chars.length; i++){
counts[chars[i] - 'a']++; //counts为一个大小为26的整型数组 存放对应每个字母出现的次数 使用前应该统一转成小写字母
}
*/
/*
* Arrays的静态函数 有sort() 和 binarySearch() 使用Arrays类需要导入java.util.Arrays 包
* sort(chars,1,3) 对chars的chars[1]到chars[3-1]的部分数组进行排序
* binarySearch(list,11) list中有11,就返回11的下标;没有,就返回-(插入点下标+1) 如-6
*/
//多维数组 int[][] array = new int[3][4]
// int[][] array = {{1,2,3},{4,5,6},{7,8,9}};
// int[][] x = new int[3][4] x.length = 3 x[i].length = 4
/*
*
*
*
*
* 实例和对象是一个意思
*
* 可以将不同的类放到同一个文件中,但是只有一个类是public的,而且该类应该与文件名相同!而且main函数应该在public类里!
*
* Circle myCircle = new Circle();
* 声明对象引用变量 myCircle
* 创建对象(存放在堆中)
* myCircle指向该对象 是Circle对象的引用。同前面数组一样,也可以说myCircle是Circle的一个对象
*
* c1 = c2 后 c1指向c2所指的对象,此时两个引用共同指向一个对象,c1原来所指对象就会成为垃圾。如果不再需要某个对象,可以给它的引用赋值为null。
*
* 数据域包括 实例变量和静态变量 实例变量和实例方法只依赖具体的对象而存在 。不同实例的实例变量不能共享,存储在内存中不同位置。而实例方法则为所有对象共同使用,但是产生的数据各自私有。
* 如果数据域的变量前有static修饰,那么就是静态变量。可以直接通过类名来调用而不用通过对象调用。(相当于全局变量)
* 静态变量又称为类变量,存在公共的内存地址,所有对象可以共享静态变量。
* 方法前有static修饰 就是静态方法,可以通过类名直接调用。
* 静态,表示 在运行之前,就要给他分配内存空间。
* final static double PI = 3.1415 类常量,加了final则不可以再修改。
*
* 类的静态方法中不能使用实例变量和实例方法(依赖于对象而存在),要想使用必须在静态方法中创建一个对象,再调用该对象的实例变量和实例方法。
* 类的实例方法中既可以使用实例变量和实例方法,又可以使用静态变量和静态方法。类的静态方法中可以使用静态变量和静态方法,但是不能 直接 使用实例变量和实例方法。
* 静态变量和静态方法既可以在类的实例方法中使用,也可以在类的静态方法中使用。但是实例变量和方法只能在实例方法中使用,不能在静态方法中使用。
* (以上为一个意思)
* public class Sytch{
int x=2000;
public static void main(String argv[]){
System.out.println("Ms "+argv[1]+"Please pay $"+x);
}
}
* 编译错误,因为main方法为静态方法,不能在里面使用实例变量。
*
* 类的私有成员变量(不管是否静态的),因为私有了,只能在类内被类的成员方法使用,在类外通过变量所属类的公有成员函数访问,需要创建一个类的对象来调用 公有 成员函数。
* 当私有成员变量是静态的情况时,由于是私有,所以只能在类内使用,因此在类内使用时可以直接用而不用通过类名调用。通过类名调用非私有静态变量或者方法的情况都是在类外调用。
* 默认修饰符将访问权限限定在包内
*
* public class Circle {
private double r = 1.0 ; //若没有初始化,则默认为0
private static int times; //times 默认为0
public double getArea() {
return r*r*Math.PI;
}
Circle(){
times++;
System.out.println(times);
}
public void plus(){
Circle c = new Circle();
}
public static void main(String[] args) {
Circle myCircle = new Circle();
myCircle.plus();
System.out.println("R is "+myCircle.r);//虽然r是私有 但是该对象在类内 因此可以访问 在此静态方法中通过创建对象使用实例变量
}
}
//运行正确 可以在类的成员方法中创建类的对象(不一定是main函数才可以,都行)
*
*
* 将对象作为参数进行传递时,传递的是引用 pass-by-sharing
*
* 对象数组:Circle[] circleArray = new Circle[10];
* circleArray[i] = new Circle();//第一句只是创建了一个数组,这个数组中放的是对象的引用,然而对象还没有创建。要创建对象,并让引用指向对象,否则会报空指针异常。
*
*
*
*
*
* 字符串:在java中,字符串是一个对象。
* String变量存储的是 对String对象的引用,String对象里存储的才是字符串的值。但和前面的数组、对象一样,一般不做区别。
*
* String s = "Java";
* s = "HTML"; 第一句创建了一个对象,s指向它,第二句又创建了一个对象,s指向它。第一个对象则没有变量指向它。
* String对象的内容是不可改变的,变化时一定是产生了新的字符串。
*
* 创建字符串的方法:
* String s1 = "Welcome to java";
* String s2 = new String("Welcome to java");
* String s3 = "Welcome to java";
*
* s1==s2 false s1==s3 true
*
* 为了提高效率并节约内存,对具有相同字符串序列的字符串直接量使用同一个实例(限定的)。
* String s1 声明了一个字符串,然后在内存中找有没有"Welcome to java",没有就创建一个。然后用s1指向该字符串对象。
* s2因为是new的 不管内存中有没有相同的,都要再重新开辟内存并创建一个。
* s3首先在内存中找有没有一样的,发现有,则s3也指向s1指向的字符串。
*
* ==:看引用的地址是否相同,即是否指向同一个对象。 equals:看字符串的内容是否相等。
*
* 字符串比较的另一个函数:compareTo
* s1.compareTo(s2) 按照字典序比较,s1小于s2,方法返回值小于0,大于返回大于0,相等返回0.(返回值实际为顺序比较,相同位置字符不同时 字符相减的差)
* 用> < = 比较字符串会发生语法错误,应该用compareTo
*
* 获取字符串长度 用s.length()函数。 数组获取长度 用a.length
* subString获取子串 subString(beginIndex,endIndex) 从begin开始 到 end-1
*
*String[] tokens = "Java#HTML#Perl".split("#",0); //tokens是字符串数组变量。数组中存放着指向每一个字符串的引用。
*for(int i = 0; i < tokens.length; i++)
* sysout(tokens[i]+" ");
*
*
* String s = String.valueOf(5.44); 将数值转化为String类型
* double d = Double.parseDouble("1.9"); 将字符串转化为数字
* String s = String.valueOf('g'); 将字符 变成 字符串
*
* 三者在执行速度方面的比较:StringBuilder > StringBuffer > String
* StringBuilder:线程非安全的 但是速度快
* StringBuffer:线程安全的 适合多线程
* StringBuilder和StringBuffer:每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不会生成新的对象。有很多修改字符串的方法,功能强大
*
* Character类:用于确定字符的类别(数字、字母、大小写)和对字符进行大小写转换。它的方法大多是静态的,Character.isLetter(s.charAt(index)) charAt(index)返回特定位置的字符的值
*
*File类:File file = new File("filename") 为以filename为名称的文件创建了一个对象,并用引用file指向该对象。
*文件对象建立之后,并不能对文件进行读取操作。 要用其他的类为 该文件对象 创建一个 能够进行读写操作的 对象
*File对象封装文件和路径属性,但是不包含从/向文件读/写数据的方法。为了进行I O操作,需要使用java IO类创建对象,这些对象包含从/向文件读/写数据的方法。
*一行一行的读取文件:
*String strbuff;
*BufferedReader data = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
*strbuff = data.readLine();(放到循环中让它一直读到最后)
*
*
*
*this引用:
*前提:如果一个局部变量和一个数据域的变量具有相同的名字,那么局部变量优先,同名的数据域变量将被隐藏。(不管数据域变量是静态还是实例变量)
*
*public class Foo{
* int i = 5;
* static double k = 0;
* void setI(int i){
* this.i = i; //由于实例变量i被局部变量i隐藏,所以需要用关键字this来引用实例变量i this指代调用实例方法的对象
* }
* static void setK(double k){
* Foo.k = k; //由于静态变量k被局部变量k隐藏,通过类名.静态变量 来引用。如果没有被隐藏,在类的内部是不需要用类名来引用的。
* }
*}
*
*this的另一个用法是 让构造方法调用同一个类的另一个构造方法。
*public Circle(double r){
* this.r = r;
*}
*public Circle(){ //此无参的构造函数去调用一个有参的构造函数 并默认设置r的值 通常无参或者参数少的构造方法用this来调用参数多的构造方法。
* this(1.0);
*}
*
*继承:
*例子:
Geo类:
/*import java.util.Date;
public class Geo{
private String color = "white";
private boolean filled;
private Date dateCreated;
int x = 5;
public Geo(){
dateCreated = new Date();
}
public Geo(String color,boolean filled){
dateCreated = new Date();
this.color = color;
this.filled = filled;
}
public String getColor() {
return color;
}
public void setColor(String color) {
System.out.println("Geo setColor called");
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
System.out.println("Geo setFilled called");
this.filled = filled;
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "created on "+dateCreated+" color:"+color+" and filled:"+filled;
}
}
Circle类:
/*import java.util.Date;
public class Circle extends Geo{
private Date dateCreated;
private String color = "White";
private boolean filled;
private double r;
public Circle(){}
public void add(){
System.out.println(x);
x = x + 1;
System.out.println(x);
System.out.println(super.x);
}
public Circle(double r){
this.r = r;
}
public Circle(String color,boolean filled,double r){
setColor(color); //如果子类重写了父类的方法,这里就是子类的方法
setFilled(filled); //即使子类隐藏了父类的属性,但是没有重写方法,仍使用父类的方法 还是不能改变自己的属性,而是改变父类的属性
this.r = r;
}
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
public double getPerimeter(){
return 2 * Math.PI * r;
}
public double getArea(){
return Math.PI * r * r;
}
public String toString() {
// TODO Auto-generated method stub
return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//如果子类继承了父类的属性,重写了属性的操作方法,但是这些子类的方法操作的还是父类的属性,因为子类根本就没有"真正"拥有这些属性。虽然有了一套自己的方法,但是操作的内容还是父类的。
//return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//子类既又定义了这些属性,又定义了属性的操作方法,可以成功改变子类的属性。
//return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//父类的属性私有,子类不能继承,就又定义了这些属性,但是没有定义操作属性的方法,还是使用继承自父类的,但是继承自父类的属性操作方法只会改变父类的属性,子类的属性依然没有改变。
//return "created on "+getDateCreated()+" color:"+getColor()+" and filled:"+isFilled();//父类的属性是私有的,子类中也没有再定义这些属性,因此子类不具有这些属性。使用继承自父类的属性get方法,显示的是父类的属性,前面set也是继承自父类,因此设置的也是父类的属性。
//return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//父类属性是可以继承的,子类中也没有隐藏这些属性,继承自父类。前面set也是继承自父类,因此设置的也是父类的属性。所以显示的是父类的属性。但看起来就好像是子类自己的。
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
public void printCircle(){
System.out.println("The circle is created "+getDateCreated()+"and the radius is:"+r);
}
} */
//Test类
/*public class Test{
public static void main(String[] args) {
Circle c = new Circle(1);
System.out.println("A circle " + c.toString());
Circle c2 = new Circle("bule",true, 2.0);//true 是小写
System.out.println("A circle " + c2.toString());
c.add();
Geo g = new Geo();
System.out.println("A Geo "+g.toString());
}
}*/
/*
*上面这个例子 说明了子类在继承了父类的属性和方法之后,这些属性和方法并不是真正属于子类的属性和方法,只是子类拥有使用的权限,每次操作这些属性和方法,改变的都是父类的。
*可以像在自己的类中那样使用,但是调用的还是父类的方法,操作的还是父类的属性。如果子类中重新定义(重写)了这些属性和方法,那么这些属性和方法就是自己的了,这时调用父类的属性和方法就要使用super.属性和super.方法
*在子类中覆盖父类方法或者隐藏父类属性(覆盖后就使用子类的方法),都只是改变了一项,仍然是操作父类!
*子类虽然继承了父类的属性和方法,但是 是和父类 共同 使用,并不是真正属于自己的。调用继承的属性和方法时,先在子类中找有没有子类覆盖(隐藏)的,没有覆盖(隐藏),就用父类的。
*
* 构造方法不能被继承,只能从子类的构造方法中用super调用
* 子类要调用父类的构造方法就必须使用super()和super(参数),而且必须在子类的第一行。
* 如果子类没有显式调用其他重载的构造方法或者父类的构造方法,编译器会自动调用super(),放在子类构造方法的第一行。
*
* 在子类中只有父类的实例方法能被继承时才能被覆盖,父类和子类的方法都能使用,默认是使用子类的方法。若想调用父类,用super.方法名
* 静态方法也能像实例方法一样被继承,但是继承之后不能被覆盖,而是被隐藏。可以使用 父类名.方法名 调用隐藏的静态方法。
*
* 无参的构造方法会被有参的构造方法覆盖。然而子类如果没有显式定义构造方法,编译器会自动调用默认的构造方法,默认的构造方法又会自动调用父类的无参构造方法,若父类没有无参构造方法,就会报错。
* 所以最好给每一个类都定义一个无参的构造方法。
*
* 多态:使用父类对象的地方都可以使用子类的对象。即父类型的变量可以引用子类型的对象。
* Object o = new Geo(); Object是声明类型 Geo是实际类型 o.方法名 调用实际类型中定义的该方法,如果实际类型中没有该方法,就去父类中找,一直到找到为止,就是动态绑定。
*
* public class FieldDemo {
* public static void main(String[] args){
* Student t = new Student("Jack");
* Person p = t;//父类创建的引用指向子类所创建的对象 向上转型
* System.out.println(t.name+","+p.name); //向上转型后可以访问被隐藏的变量。
* System.out.println(t.getName()+","+p.getName()); //向上转型后不能访问被覆盖的方法 p.getName()仍然是子类的方法。
* }
* }
* class Person{
* String name;
* int age;
* public String getName(){
* return this.name;
* }
* }
* class Student extends Person{
* String name; // 属性和父类属性名相同,但在做开发时一般不会和父类属性名相同!!
* public Student(String name){
* this.name = name;
* super.name = "Rose"; // 为父类中的属性赋值
* }
* public String getName(){
* return this.name;
* }
* }
* 返回结果是:Jack,Rose
* Jack,Jack 在Java中,属性绑定到类型,方法绑定到对象!在向上转型的情况下,调用对象的方法时是子类的方法,而调用对象的属性时还是父类的属性。
* 父类的方法被覆盖,属性被隐藏。向上转型后,可以访问被隐藏的变量,但是不能访问被覆盖的方法。
* 对象转换: Object o = new Student()为隐式转换
* Student s = (Student) o 显式转换
* Object o = new Circle();
* if(o instanceof Circle){ //源对象是目标类的实例时才能进行类型转换
* sysout("The cirlce diameter is:"+((Circle)o).getDiameter(); // .优先于类型转换符()
* }
*
*运行时(动态)绑定针对的范畴只是对象的方法。执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
*
* 隐藏:若B隐藏了A的变量或方法,那么B不能访问A被隐藏的变量或方法,但将B转换成A后可以访问A被隐藏的变量或者方法。
* 覆盖:若B覆盖了A的方法,那么不仅B不能访问A被覆盖的方法,将B转换成A后同样不能访问A被覆盖的方法。
*
*一、父类的实例变量和类变量能被子类的同名变量隐藏。
*二、父类的静态方法被子类的同名静态方法隐藏,父类的实例方法被子类的同名实例方法覆盖。
*三、变量只能被隐藏不会被覆盖,子类的实例变量可以隐藏父类的类变量,子类的类变量也可以隐藏父类的实例变量。
*
*覆盖的处理方法就是在调用该方法时,调用实际对象的方法。隐藏的处理方法就是根据调用方法所属的类来识别、调用不同的方法。隐藏是一个“编译时”的概念,覆盖是运行时的概念。
*
*Object的equals方法是使用 == 实现的,用于比较两个引用变量是否指向同一个对象。与String是不一样的,因为String改写了equals方法 让它去比较内容了,这样功能不重复。
*
*
*ArrayList类:
*ArrayList cityList = new ArrayList();
*cityList可以调用ArrayList类的多种实例方法,如add() get() contains()
*
*默认的数据和方法修饰符是 同类和同包其他类可以访问
*
*
*
*抽象类:
*抽象类不能创建实例,抽象方法只有定义没有实现,只要有抽象方法的类 就必须声明为抽象类。
*抽象类的子类一定要覆盖实现抽象方法,否则没有意义。后面才能动态绑定。不同的子类 和父类都有同名的方法,声明类型为父类的 对象,在调用时对象才能根据实际类型到底是哪个子类进行调用。
*Geo o1 = new Circle(3);
Geo o2 = new Rectangle(5,3);
public static void display1(Geo o){//体现了复用,但是没有体现动态绑定
if(o instanceof Circle){
System.out.println(((Circle)o).getArea());
}
else if(o instanceof Rectangle)
System.out.println(((Rectangle)o).getArea());
}
public static void display(Geo o){ //复用,加 动态绑定
System.out.println(o.getArea());
}
*把Geo定义为抽象类之后,在Test类中直接使用o.getArea()即可,不用像第一个那样麻烦。
*由于多态的性质,即可以在运行中根据实际类型选择调用方法(动态绑定),所以抽象类可以利用这个特点,进行简便操作。
*如果在抽象类中没有想要用的抽象方法,每次从调用时都要先进行显式转换,再调用子类的方法。抽象类则根据动态绑定根据传入对象的实际类型选择正确的方法。
*
*注:1.抽象方法不能包含在非抽象类中(含有抽象方法,就一定是抽象类!)抽象类的子类如果没有实现所有的抽象方法,那么子类必须定义为抽象的。
*2.即使子类的父类是具体的,这个子类也可以是抽象的。
*3.不能使用new操作符从一个抽象类创建一个实例,但是抽象类可以用作一种数据类型。比如对象数组。
* Geo[] o = new Geo[10]
* o[0] = new Circle();
*
* 接口:
* interface Edible { //接口 可以和其他类 定义在同一个文件中
* String howToEat(); //接口中的常量是final static的,方法是抽象的。默认如此,因此在声明时不必特意写这个,可以忽略这些修饰符。
* }
*
* abstract class Fruit implements Edible{//由于继承了接口,但是并没有实现接口中的方法,因此应为抽象类。
*
* }
* class Apple extends Fruit{ //父类Fruit继承了接口,但是没有实现接口中的方法。子类Apple继承了父类Fruit之后,如果不实现接口中的方法,就要声明为抽象类,因为接口中的方法是抽象的。
* public String howToEat(){
* return "Apple:Make apple cider.";
* }
* }
* class Orange extends Fruit{
* public String howToEat(){
* return "Orange:Make orange juice.";
* }
* }
*
* class Animal{
*
* }
* class Chicken extends Animal implements Edible{//子类Chicken自己继承了接口
* public String howToEat(){
* return "Chicken: Fry it.";
* }
* }
* class Tiger extends Animal{
*
* }
*
* public class TestEdible {
* public static void main(String[] args) {
* /* Edible[] objects = {new Chicken(),new Apple(),new Orange()};//这里对象必须都是实现了接口中方法的类,继承了但是没有覆盖的类不行(因为这种是抽象类,不能new)
* for(int i = 0;i < objects.length;i++){
* System.out.println(objects[i].howToEat());//动态绑定 继承了接口的这些方法覆盖实现了接口中的方法。
* } //这是一种方式。动态绑定
*
*
* /* Object[] objects = {new Chicken(),new Apple(),new Orange()};
* for(int i = 0;i < objects.length;i++){
* if(objects[i] instanceof Edible) //先看看对象有没有继承Edible接口 //直接用objects[i].howToEat() 是错的。
* System.out.println(((Edible) objects[i]).howToEat()); //由于Object类(声明类型)中 没有定义howToEat()方法,所以不符合动态绑定的情况。因此要进行显示转换。
* //抽象类中的动态绑定是单线继承的,在最原始的抽象父类Geo中含有抽象方法,该线下的所有子类都有,可以用原始类声明的对象来进行动态动态绑定。
* } //这是另一种方式
* }
* }
*
*
*Comparable接口:
*
*ComparableRectangle cR1 = new ComparableRectangle(1,2);
*ComparableRectangle cR2 = new ComparableRectangle(3,4);
*max(cR1,cR2)
*
*public static Comparable max(Comparable o1,Comparable o2){
* if(o1.compareTo(o2) > 0)
* return o1;
* else return o2;
* }
* //在ComparableRectangle类中重写Comparable接口的CompareTo方法
*public int compareTo(Object o) { //现在ComparableRectangle类的对象 就拥有了能够和其它对象 比较面积的方法
* if(getArea() > ((ComparableRectangle)o).getArea()) //当前对象的面积大于要比较对象o的面积 o声明类型是Object,要转换为ComparableRectangle类型
* return 1; //其实用的是getArea() 得到父类的面积,前面的构造函数也是用的父类的super(width,length)设置的父类的属性
* else if(getArea() < ((ComparableRectangle)o).getArea())//这些方法和属性都是可以随便用的(相当于自己的)
* return -1;
* else return 0;
* }
*
*Java只允许为类的扩展做单一继承,但是允许使用接口做多重扩展。
*类的关系是 强是关系,父子关系。
*接口是对 弱是关系 进行建模的,只要这些类的对象拥有某种属性,就可以实现该接口,相比继承类来说更加灵活。实现某一个接口的类不一定是同一类,它们只是有同样的属性。
*
*Cloneable接口:
*这个接口是空的:
*public interface Cloneable{}
*
*Calendar calendar = new GregorianCalendar(2003,2,1);
*Calendar calendar1 = calendar;
*Calendar calendar2 = (Calendar)calendar.clone();//创建了一个新的对象 用calendar2指向它
*
*calendar == calendar1 true
*calendar == calendar2 flase
*calendar.equals(calendar2) true
*克隆的calendar2 内容和calendar一样 但是是不同的对象
*
*如果一个类继承了Cloneable接口 就必须覆盖Object类中的clone()方法。
*public Object clone(){
* return super.clone();
*}
*
*House house = new House(1,175);
*House house1 = (House)house.clone();//返回的是Object类型的,但是实际类型是Houese 需要进行显式转换
*house和house1是内容相同的不同对象,Object类的clone()方法将原始对象的每个数据域复制给目标对象。
*如果数据域中的某个属性是基本类型,复制的就是它的值。
*如果数据域中的某个属性是 对象,复制是该对象的引用,不是对象的内容。(浅复制)
*
*为了克隆一个对象,该对象的类必须覆盖clone()方法并继承Cloneable接口。
* class Person implements Cloneable{
private int age;
private String name;
private Date birth;
public Person(int a,String n,Date b){
age=a;
name=n;
birth=b;
}
public static void main(String[] args){
Person kobe=new Person(33,"kobe",new Date());
Person kobe_bak=kobe.clone();
}
@Override
public Object clone(){
Person p=(Person)super.clone(); //返回的是Object类型的,但实际类型是Person类 需要进行显式转换
//Date类型的birth域是可变的,需要对其克隆,进行深拷贝
//Date类实现的克隆,直接调用即可
p.birth=this.birth.clone();
return p;
}
}
*
*包装类:
*许多Java的方法需要对象作为参数。例如,在ArrayList类中的add(object)方法向ArrayList中添加一个对象。因此要将基本数据类型并入对象或包装成对象,这种类就是包装类。
*这样可以利用通用程序设计。
*
*doubleValue(),floatValue(),intValue(),将对象转换为基本类型值。
*Integer I = new Integer(45);//这里也可以是"45" 字符串和数值都行 生成一个包装类对象I
*int i = I.intValue();//将对象I转换为基本数据类型值
*System.out.println(i);
*
*valueOf(String s)方法:
*Double doubleObject = Double.valueOf("12.4")创建一个对象,并初始化为指定字符串表示的值。
*
*包装类的静态方法parseInt,parseDouble等,直接将数值字符串转化为数值。默认为十进制。
*parseInt(String s,int radix),parseDouble(String s,int radix):
*Integer.parseInt("11",2) return 3
*Integer.parseInt("12",8) return 10
*Integer.parseInt("13",10) return 13
*
*Double类有parseDouble方法
*
*int j = Integer.parseInt("23");//parseInt返回数值
*System.out.println(j);
*
*Integer I1 = Integer.valueOf("12");//valueOf返回一个创建的对象
*
*都是静态方法。
*
*
*二进制IO和文本IO:
*
*对于文件内容的操作主要分为两大类.分别是:字符流,字节流.
*
* 其中,字符流有两个抽象类:Writer Reader (文本IO)
* 其对应子类FileWriter和FileReader可实现文件的读写操作
* BufferedWriter和BufferedReader能够提供缓冲区功能,用以提高效率.
*
* 同样,字节流也有两个抽象类:InputStream OutputStream (二进制IO)
* 其对应子类有FileInputStream和FileOutputStream实现文件读写
* BufferedInputStream和BufferedOutputStream提供缓冲区功能
*
* File对象封装文件或者路径的属性,但是不包含从\向文件读\写数据的方法,需要使用Java的IO类创建对象,这些对象包含从\向文件读\写数据的方法。
* File不属于文件流 , 只能代表一个文件或是目录的路径名而已。
*
* InputStream和OutputStream是二进制IO的根类:
* FileInputStream(file对象 或 String类型的filename)和FileOutputSream(file对象 或 String类型的filename)
*
* InputStream:FileInputSream,FilterInputStream,ObjectInputStream
* OutputStream:FileOutputSream,FilterOutputStream,ObjectOutputStream
* 它们都有read()或者write()方法。
*
* ObjectInputStream和ObjectOutputStream可以实现对对象的输入和输出。
* ObjectInputStream input = new ObjectInputStream(new FileInputSream("Object.dat"));
*可序列化:
* Java中ObjectInputStream 与 ObjectOutputStream这两个包装类可用于输入流中读取对象类数据和将对象类型的数据写入到底层输入流 。
* ObjectInputStream 与 ObjectOutputStream 类所读写的对象必须实现了 Serializable 接口。
* 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。
* 虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
* 当 通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。
* Foo myFoo = new Foo();
* myFoo .setWidth(37);
* myFoo.setHeight(70);
*
* FileOutputStream fs = new FileOutputStream("foo.ser");
* ObjectOutputStream os = new ObjectOutputStream(fs);
* os.writeObject(myFoo);
*
*
*BufferedXXXX 设立缓冲区,减少读写次数来提高输入和输出速度。
*
*当使用read()方法时,实际上是先读取buf中的数据,而不是直接对数据来源作读取。
*当buf中的数据不足时,BufferedInputStream才会再实现给定的InputStream对象的read()方法,从指定的装置中提取数据。
*
*当使用write()方法写入数据时实际上会先将数据写到buf中。
*当buf已满时才会实现给定的OutputStream对象的write()方法,将buf数据写到目的地,而不是每次都对目的地作写入的动作。
*
*BufferedReader data = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
*FileInputStream读入的是字节流,InputStreamReader将字节流转换为字符流 然后配合BufferedReader.
*BufferedReader类就是一个包装类,它可以包装字符流,将字符流放入缓存里,先把字符读到缓存里,到缓存满了或者你flush的时候,再读入内存,就是为了提供读的效率而设计的。
*BufferedReader由Reader类扩展而来,提供通用的缓冲方式文本读取,而且提供了很实用的readLine,读取分行文本很适合,BufferedReader是针对Reader的,不直接针对文件,也不是只针对文件读取。
*
*FileInputStream 类介绍:
* 以字节为单位的流处理。字节序列:二进制数据。与编码无关,不存在乱码问题。
* FileInputStream 类的主要方法有:
* Read(), read(byte[] b), read(byte[],int off,int len),available();
*
* FileInputStream 类与 FileReader 类的区别:
* 两个类的构造函数的形式和参数都是相同的,参数为 File 对象或者表示路径的 String ,它们到底有何区别呢?
* FileInputStream :以字节流方式读取;
* FileReader :把文件转换为字符流读入;
* InputStream提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。
* Reader类及其子类提供的字符流的读取char,inputStream及其子类提供字节流的读取byte,所以FileReader类是将文件按字符流的方式读取,FileInputStream则按字节流的方式读取文件;
* InputStreamReader可以将读如stream转换成字符流方式,是reader和stream之间的桥梁。
*
*
*
*
*
*
*/
/*
* void方法不需要return语句,但它能用于终止方法并返回到方法的调用者 提前返回。
* 有无return语句都会返回到调用函数,非void的函数会捎带一个返回数而已。
*/
/* class Function {
public static void printGrade(double score){
if(score < 0 || score > 100){
System.out.println("Invalid score");
return; //提前返回到调用函数
}
...
* ...
}
}*/
/*
* double[] myList = new double[10];
* 先声明一个数组,并不分配空间
* 创建数组,并分配空间
* myList指向该数组 myList只是这个数组的引用
*
* 增强for循环遍历:对myList中的每个元素u进行以下操作,但是不能进行修改数组中元素或者改变遍历顺序
* for(double u : myList){
* System.out.println(u);
* }
*
* list2 = list1; list1的引用值复制给list2 此时list2指向了list1 两个数组的内容相同,但并没有复制数组
*
* 给方法传递参数时:如果参数类型是基本类型 则pass-by-value;如果参数类型是数组类型,则pass-by-sharing,即传递的是数组的引用。
* public class Test{
public static void main(String[] args) {
int x = 1;
int[] y = new int[10];
System.out.println("y[0] is:"+y[0]);
m(x,y);
System.out.println("x is:"+x);
System.out.println("y[0] is:"+y[0]);
}
public static void m(int number,int[] array){ //相当于 int[] array = y
number = 2;
array[0] = 3;
}
}
*/
/*
*既可以向方法传递一个数组 也可以从方法返回一个数组
*/
/* public class Test{
public static int[] reverse(int[] list) { //注意函数的类型为 int[]
int[] result = new int[list.length];
for(int i = 0, j = result.length - 1;i < list.length;i++,j--)
result[i] = list[j];
return result; //返回值类型为 int[]
}
}
*/
//统计一个字符数组中每个字母出现的次数 字符数组 char[] ch = new char[100]或者 char[] ch = {'a','b','c'};
/* for(int i = 0;i < chars.length; i++){
counts[chars[i] - 'a']++; //counts为一个大小为26的整型数组 存放对应每个字母出现的次数 使用前应该统一转成小写字母
}
*/
/*
* Arrays的静态函数 有sort() 和 binarySearch() 使用Arrays类需要导入java.util.Arrays 包
* sort(chars,1,3) 对chars的chars[1]到chars[3-1]的部分数组进行排序
* binarySearch(list,11) list中有11,就返回11的下标;没有,就返回-(插入点下标+1) 如-6
*/
//多维数组 int[][] array = new int[3][4]
// int[][] array = {{1,2,3},{4,5,6},{7,8,9}};
// int[][] x = new int[3][4] x.length = 3 x[i].length = 4
/*
*
*
*
*
* 实例和对象是一个意思
*
* 可以将不同的类放到同一个文件中,但是只有一个类是public的,而且该类应该与文件名相同!而且main函数应该在public类里!
*
* Circle myCircle = new Circle();
* 声明对象引用变量 myCircle
* 创建对象(存放在堆中)
* myCircle指向该对象 是Circle对象的引用。同前面数组一样,也可以说myCircle是Circle的一个对象
*
* c1 = c2 后 c1指向c2所指的对象,此时两个引用共同指向一个对象,c1原来所指对象就会成为垃圾。如果不再需要某个对象,可以给它的引用赋值为null。
*
* 数据域包括 实例变量和静态变量 实例变量和实例方法只依赖具体的对象而存在 。不同实例的实例变量不能共享,存储在内存中不同位置。而实例方法则为所有对象共同使用,但是产生的数据各自私有。
* 如果数据域的变量前有static修饰,那么就是静态变量。可以直接通过类名来调用而不用通过对象调用。(相当于全局变量)
* 静态变量又称为类变量,存在公共的内存地址,所有对象可以共享静态变量。
* 方法前有static修饰 就是静态方法,可以通过类名直接调用。
* 静态,表示 在运行之前,就要给他分配内存空间。
* final static double PI = 3.1415 类常量,加了final则不可以再修改。
*
* 类的静态方法中不能使用实例变量和实例方法(依赖于对象而存在),要想使用必须在静态方法中创建一个对象,再调用该对象的实例变量和实例方法。
* 类的实例方法中既可以使用实例变量和实例方法,又可以使用静态变量和静态方法。类的静态方法中可以使用静态变量和静态方法,但是不能 直接 使用实例变量和实例方法。
* 静态变量和静态方法既可以在类的实例方法中使用,也可以在类的静态方法中使用。但是实例变量和方法只能在实例方法中使用,不能在静态方法中使用。
* (以上为一个意思)
* public class Sytch{
int x=2000;
public static void main(String argv[]){
System.out.println("Ms "+argv[1]+"Please pay $"+x);
}
}
* 编译错误,因为main方法为静态方法,不能在里面使用实例变量。
*
* 类的私有成员变量(不管是否静态的),因为私有了,只能在类内被类的成员方法使用,在类外通过变量所属类的公有成员函数访问,需要创建一个类的对象来调用 公有 成员函数。
* 当私有成员变量是静态的情况时,由于是私有,所以只能在类内使用,因此在类内使用时可以直接用而不用通过类名调用。通过类名调用非私有静态变量或者方法的情况都是在类外调用。
* 默认修饰符将访问权限限定在包内
*
* public class Circle {
private double r = 1.0 ; //若没有初始化,则默认为0
private static int times; //times 默认为0
public double getArea() {
return r*r*Math.PI;
}
Circle(){
times++;
System.out.println(times);
}
public void plus(){
Circle c = new Circle();
}
public static void main(String[] args) {
Circle myCircle = new Circle();
myCircle.plus();
System.out.println("R is "+myCircle.r);//虽然r是私有 但是该对象在类内 因此可以访问 在此静态方法中通过创建对象使用实例变量
}
}
//运行正确 可以在类的成员方法中创建类的对象(不一定是main函数才可以,都行)
*
*
* 将对象作为参数进行传递时,传递的是引用 pass-by-sharing
*
* 对象数组:Circle[] circleArray = new Circle[10];
* circleArray[i] = new Circle();//第一句只是创建了一个数组,这个数组中放的是对象的引用,然而对象还没有创建。要创建对象,并让引用指向对象,否则会报空指针异常。
*
*
*
*
*
* 字符串:在java中,字符串是一个对象。
* String变量存储的是 对String对象的引用,String对象里存储的才是字符串的值。但和前面的数组、对象一样,一般不做区别。
*
* String s = "Java";
* s = "HTML"; 第一句创建了一个对象,s指向它,第二句又创建了一个对象,s指向它。第一个对象则没有变量指向它。
* String对象的内容是不可改变的,变化时一定是产生了新的字符串。
*
* 创建字符串的方法:
* String s1 = "Welcome to java";
* String s2 = new String("Welcome to java");
* String s3 = "Welcome to java";
*
* s1==s2 false s1==s3 true
*
* 为了提高效率并节约内存,对具有相同字符串序列的字符串直接量使用同一个实例(限定的)。
* String s1 声明了一个字符串,然后在内存中找有没有"Welcome to java",没有就创建一个。然后用s1指向该字符串对象。
* s2因为是new的 不管内存中有没有相同的,都要再重新开辟内存并创建一个。
* s3首先在内存中找有没有一样的,发现有,则s3也指向s1指向的字符串。
*
* ==:看引用的地址是否相同,即是否指向同一个对象。 equals:看字符串的内容是否相等。
*
* 字符串比较的另一个函数:compareTo
* s1.compareTo(s2) 按照字典序比较,s1小于s2,方法返回值小于0,大于返回大于0,相等返回0.(返回值实际为顺序比较,相同位置字符不同时 字符相减的差)
* 用> < = 比较字符串会发生语法错误,应该用compareTo
*
* 获取字符串长度 用s.length()函数。 数组获取长度 用a.length
* subString获取子串 subString(beginIndex,endIndex) 从begin开始 到 end-1
*
*String[] tokens = "Java#HTML#Perl".split("#",0); //tokens是字符串数组变量。数组中存放着指向每一个字符串的引用。
*for(int i = 0; i < tokens.length; i++)
* sysout(tokens[i]+" ");
*
*
* String s = String.valueOf(5.44); 将数值转化为String类型
* double d = Double.parseDouble("1.9"); 将字符串转化为数字
* String s = String.valueOf('g'); 将字符 变成 字符串
*
* 三者在执行速度方面的比较:StringBuilder > StringBuffer > String
* StringBuilder:线程非安全的 但是速度快
* StringBuffer:线程安全的 适合多线程
* StringBuilder和StringBuffer:每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不会生成新的对象。有很多修改字符串的方法,功能强大
*
* Character类:用于确定字符的类别(数字、字母、大小写)和对字符进行大小写转换。它的方法大多是静态的,Character.isLetter(s.charAt(index)) charAt(index)返回特定位置的字符的值
*
*File类:File file = new File("filename") 为以filename为名称的文件创建了一个对象,并用引用file指向该对象。
*文件对象建立之后,并不能对文件进行读取操作。 要用其他的类为 该文件对象 创建一个 能够进行读写操作的 对象
*File对象封装文件和路径属性,但是不包含从/向文件读/写数据的方法。为了进行I O操作,需要使用java IO类创建对象,这些对象包含从/向文件读/写数据的方法。
*一行一行的读取文件:
*String strbuff;
*BufferedReader data = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
*strbuff = data.readLine();(放到循环中让它一直读到最后)
*
*
*
*this引用:
*前提:如果一个局部变量和一个数据域的变量具有相同的名字,那么局部变量优先,同名的数据域变量将被隐藏。(不管数据域变量是静态还是实例变量)
*
*public class Foo{
* int i = 5;
* static double k = 0;
* void setI(int i){
* this.i = i; //由于实例变量i被局部变量i隐藏,所以需要用关键字this来引用实例变量i this指代调用实例方法的对象
* }
* static void setK(double k){
* Foo.k = k; //由于静态变量k被局部变量k隐藏,通过类名.静态变量 来引用。如果没有被隐藏,在类的内部是不需要用类名来引用的。
* }
*}
*
*this的另一个用法是 让构造方法调用同一个类的另一个构造方法。
*public Circle(double r){
* this.r = r;
*}
*public Circle(){ //此无参的构造函数去调用一个有参的构造函数 并默认设置r的值 通常无参或者参数少的构造方法用this来调用参数多的构造方法。
* this(1.0);
*}
*
*继承:
*例子:
Geo类:
/*import java.util.Date;
public class Geo{
private String color = "white";
private boolean filled;
private Date dateCreated;
int x = 5;
public Geo(){
dateCreated = new Date();
}
public Geo(String color,boolean filled){
dateCreated = new Date();
this.color = color;
this.filled = filled;
}
public String getColor() {
return color;
}
public void setColor(String color) {
System.out.println("Geo setColor called");
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
System.out.println("Geo setFilled called");
this.filled = filled;
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "created on "+dateCreated+" color:"+color+" and filled:"+filled;
}
}
Circle类:
/*import java.util.Date;
public class Circle extends Geo{
private Date dateCreated;
private String color = "White";
private boolean filled;
private double r;
public Circle(){}
public void add(){
System.out.println(x);
x = x + 1;
System.out.println(x);
System.out.println(super.x);
}
public Circle(double r){
this.r = r;
}
public Circle(String color,boolean filled,double r){
setColor(color); //如果子类重写了父类的方法,这里就是子类的方法
setFilled(filled); //即使子类隐藏了父类的属性,但是没有重写方法,仍使用父类的方法 还是不能改变自己的属性,而是改变父类的属性
this.r = r;
}
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
public double getPerimeter(){
return 2 * Math.PI * r;
}
public double getArea(){
return Math.PI * r * r;
}
public String toString() {
// TODO Auto-generated method stub
return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//如果子类继承了父类的属性,重写了属性的操作方法,但是这些子类的方法操作的还是父类的属性,因为子类根本就没有"真正"拥有这些属性。虽然有了一套自己的方法,但是操作的内容还是父类的。
//return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//子类既又定义了这些属性,又定义了属性的操作方法,可以成功改变子类的属性。
//return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//父类的属性私有,子类不能继承,就又定义了这些属性,但是没有定义操作属性的方法,还是使用继承自父类的,但是继承自父类的属性操作方法只会改变父类的属性,子类的属性依然没有改变。
//return "created on "+getDateCreated()+" color:"+getColor()+" and filled:"+isFilled();//父类的属性是私有的,子类中也没有再定义这些属性,因此子类不具有这些属性。使用继承自父类的属性get方法,显示的是父类的属性,前面set也是继承自父类,因此设置的也是父类的属性。
//return "created on "+dateCreated+" color:"+color+" and filled:"+filled;//父类属性是可以继承的,子类中也没有隐藏这些属性,继承自父类。前面set也是继承自父类,因此设置的也是父类的属性。所以显示的是父类的属性。但看起来就好像是子类自己的。
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
public void printCircle(){
System.out.println("The circle is created "+getDateCreated()+"and the radius is:"+r);
}
} */
//Test类
/*public class Test{
public static void main(String[] args) {
Circle c = new Circle(1);
System.out.println("A circle " + c.toString());
Circle c2 = new Circle("bule",true, 2.0);//true 是小写
System.out.println("A circle " + c2.toString());
c.add();
Geo g = new Geo();
System.out.println("A Geo "+g.toString());
}
}*/
/*
*上面这个例子 说明了子类在继承了父类的属性和方法之后,这些属性和方法并不是真正属于子类的属性和方法,只是子类拥有使用的权限,每次操作这些属性和方法,改变的都是父类的。
*可以像在自己的类中那样使用,但是调用的还是父类的方法,操作的还是父类的属性。如果子类中重新定义(重写)了这些属性和方法,那么这些属性和方法就是自己的了,这时调用父类的属性和方法就要使用super.属性和super.方法
*在子类中覆盖父类方法或者隐藏父类属性(覆盖后就使用子类的方法),都只是改变了一项,仍然是操作父类!
*子类虽然继承了父类的属性和方法,但是 是和父类 共同 使用,并不是真正属于自己的。调用继承的属性和方法时,先在子类中找有没有子类覆盖(隐藏)的,没有覆盖(隐藏),就用父类的。
*
* 构造方法不能被继承,只能从子类的构造方法中用super调用
* 子类要调用父类的构造方法就必须使用super()和super(参数),而且必须在子类的第一行。
* 如果子类没有显式调用其他重载的构造方法或者父类的构造方法,编译器会自动调用super(),放在子类构造方法的第一行。
*
* 在子类中只有父类的实例方法能被继承时才能被覆盖,父类和子类的方法都能使用,默认是使用子类的方法。若想调用父类,用super.方法名
* 静态方法也能像实例方法一样被继承,但是继承之后不能被覆盖,而是被隐藏。可以使用 父类名.方法名 调用隐藏的静态方法。
*
* 无参的构造方法会被有参的构造方法覆盖。然而子类如果没有显式定义构造方法,编译器会自动调用默认的构造方法,默认的构造方法又会自动调用父类的无参构造方法,若父类没有无参构造方法,就会报错。
* 所以最好给每一个类都定义一个无参的构造方法。
*
* 多态:使用父类对象的地方都可以使用子类的对象。即父类型的变量可以引用子类型的对象。
* Object o = new Geo(); Object是声明类型 Geo是实际类型 o.方法名 调用实际类型中定义的该方法,如果实际类型中没有该方法,就去父类中找,一直到找到为止,就是动态绑定。
*
* public class FieldDemo {
* public static void main(String[] args){
* Student t = new Student("Jack");
* Person p = t;//父类创建的引用指向子类所创建的对象 向上转型
* System.out.println(t.name+","+p.name); //向上转型后可以访问被隐藏的变量。
* System.out.println(t.getName()+","+p.getName()); //向上转型后不能访问被覆盖的方法 p.getName()仍然是子类的方法。
* }
* }
* class Person{
* String name;
* int age;
* public String getName(){
* return this.name;
* }
* }
* class Student extends Person{
* String name; // 属性和父类属性名相同,但在做开发时一般不会和父类属性名相同!!
* public Student(String name){
* this.name = name;
* super.name = "Rose"; // 为父类中的属性赋值
* }
* public String getName(){
* return this.name;
* }
* }
* 返回结果是:Jack,Rose
* Jack,Jack 在Java中,属性绑定到类型,方法绑定到对象!在向上转型的情况下,调用对象的方法时是子类的方法,而调用对象的属性时还是父类的属性。
* 父类的方法被覆盖,属性被隐藏。向上转型后,可以访问被隐藏的变量,但是不能访问被覆盖的方法。
* 对象转换: Object o = new Student()为隐式转换
* Student s = (Student) o 显式转换
* Object o = new Circle();
* if(o instanceof Circle){ //源对象是目标类的实例时才能进行类型转换
* sysout("The cirlce diameter is:"+((Circle)o).getDiameter(); // .优先于类型转换符()
* }
*
*运行时(动态)绑定针对的范畴只是对象的方法。执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
*
* 隐藏:若B隐藏了A的变量或方法,那么B不能访问A被隐藏的变量或方法,但将B转换成A后可以访问A被隐藏的变量或者方法。
* 覆盖:若B覆盖了A的方法,那么不仅B不能访问A被覆盖的方法,将B转换成A后同样不能访问A被覆盖的方法。
*
*一、父类的实例变量和类变量能被子类的同名变量隐藏。
*二、父类的静态方法被子类的同名静态方法隐藏,父类的实例方法被子类的同名实例方法覆盖。
*三、变量只能被隐藏不会被覆盖,子类的实例变量可以隐藏父类的类变量,子类的类变量也可以隐藏父类的实例变量。
*
*覆盖的处理方法就是在调用该方法时,调用实际对象的方法。隐藏的处理方法就是根据调用方法所属的类来识别、调用不同的方法。隐藏是一个“编译时”的概念,覆盖是运行时的概念。
*
*Object的equals方法是使用 == 实现的,用于比较两个引用变量是否指向同一个对象。与String是不一样的,因为String改写了equals方法 让它去比较内容了,这样功能不重复。
*
*
*ArrayList类:
*ArrayList cityList = new ArrayList();
*cityList可以调用ArrayList类的多种实例方法,如add() get() contains()
*
*默认的数据和方法修饰符是 同类和同包其他类可以访问
*
*
*
*抽象类:
*抽象类不能创建实例,抽象方法只有定义没有实现,只要有抽象方法的类 就必须声明为抽象类。
*抽象类的子类一定要覆盖实现抽象方法,否则没有意义。后面才能动态绑定。不同的子类 和父类都有同名的方法,声明类型为父类的 对象,在调用时对象才能根据实际类型到底是哪个子类进行调用。
*Geo o1 = new Circle(3);
Geo o2 = new Rectangle(5,3);
public static void display1(Geo o){//体现了复用,但是没有体现动态绑定
if(o instanceof Circle){
System.out.println(((Circle)o).getArea());
}
else if(o instanceof Rectangle)
System.out.println(((Rectangle)o).getArea());
}
public static void display(Geo o){ //复用,加 动态绑定
System.out.println(o.getArea());
}
*把Geo定义为抽象类之后,在Test类中直接使用o.getArea()即可,不用像第一个那样麻烦。
*由于多态的性质,即可以在运行中根据实际类型选择调用方法(动态绑定),所以抽象类可以利用这个特点,进行简便操作。
*如果在抽象类中没有想要用的抽象方法,每次从调用时都要先进行显式转换,再调用子类的方法。抽象类则根据动态绑定根据传入对象的实际类型选择正确的方法。
*
*注:1.抽象方法不能包含在非抽象类中(含有抽象方法,就一定是抽象类!)抽象类的子类如果没有实现所有的抽象方法,那么子类必须定义为抽象的。
*2.即使子类的父类是具体的,这个子类也可以是抽象的。
*3.不能使用new操作符从一个抽象类创建一个实例,但是抽象类可以用作一种数据类型。比如对象数组。
* Geo[] o = new Geo[10]
* o[0] = new Circle();
*
* 接口:
* interface Edible { //接口 可以和其他类 定义在同一个文件中
* String howToEat(); //接口中的常量是final static的,方法是抽象的。默认如此,因此在声明时不必特意写这个,可以忽略这些修饰符。
* }
*
* abstract class Fruit implements Edible{//由于继承了接口,但是并没有实现接口中的方法,因此应为抽象类。
*
* }
* class Apple extends Fruit{ //父类Fruit继承了接口,但是没有实现接口中的方法。子类Apple继承了父类Fruit之后,如果不实现接口中的方法,就要声明为抽象类,因为接口中的方法是抽象的。
* public String howToEat(){
* return "Apple:Make apple cider.";
* }
* }
* class Orange extends Fruit{
* public String howToEat(){
* return "Orange:Make orange juice.";
* }
* }
*
* class Animal{
*
* }
* class Chicken extends Animal implements Edible{//子类Chicken自己继承了接口
* public String howToEat(){
* return "Chicken: Fry it.";
* }
* }
* class Tiger extends Animal{
*
* }
*
* public class TestEdible {
* public static void main(String[] args) {
* /* Edible[] objects = {new Chicken(),new Apple(),new Orange()};//这里对象必须都是实现了接口中方法的类,继承了但是没有覆盖的类不行(因为这种是抽象类,不能new)
* for(int i = 0;i < objects.length;i++){
* System.out.println(objects[i].howToEat());//动态绑定 继承了接口的这些方法覆盖实现了接口中的方法。
* } //这是一种方式。动态绑定
*
*
* /* Object[] objects = {new Chicken(),new Apple(),new Orange()};
* for(int i = 0;i < objects.length;i++){
* if(objects[i] instanceof Edible) //先看看对象有没有继承Edible接口 //直接用objects[i].howToEat() 是错的。
* System.out.println(((Edible) objects[i]).howToEat()); //由于Object类(声明类型)中 没有定义howToEat()方法,所以不符合动态绑定的情况。因此要进行显示转换。
* //抽象类中的动态绑定是单线继承的,在最原始的抽象父类Geo中含有抽象方法,该线下的所有子类都有,可以用原始类声明的对象来进行动态动态绑定。
* } //这是另一种方式
* }
* }
*
*
*Comparable接口:
*
*ComparableRectangle cR1 = new ComparableRectangle(1,2);
*ComparableRectangle cR2 = new ComparableRectangle(3,4);
*max(cR1,cR2)
*
*public static Comparable max(Comparable o1,Comparable o2){
* if(o1.compareTo(o2) > 0)
* return o1;
* else return o2;
* }
* //在ComparableRectangle类中重写Comparable接口的CompareTo方法
*public int compareTo(Object o) { //现在ComparableRectangle类的对象 就拥有了能够和其它对象 比较面积的方法
* if(getArea() > ((ComparableRectangle)o).getArea()) //当前对象的面积大于要比较对象o的面积 o声明类型是Object,要转换为ComparableRectangle类型
* return 1; //其实用的是getArea() 得到父类的面积,前面的构造函数也是用的父类的super(width,length)设置的父类的属性
* else if(getArea() < ((ComparableRectangle)o).getArea())//这些方法和属性都是可以随便用的(相当于自己的)
* return -1;
* else return 0;
* }
*
*Java只允许为类的扩展做单一继承,但是允许使用接口做多重扩展。
*类的关系是 强是关系,父子关系。
*接口是对 弱是关系 进行建模的,只要这些类的对象拥有某种属性,就可以实现该接口,相比继承类来说更加灵活。实现某一个接口的类不一定是同一类,它们只是有同样的属性。
*
*Cloneable接口:
*这个接口是空的:
*public interface Cloneable{}
*
*Calendar calendar = new GregorianCalendar(2003,2,1);
*Calendar calendar1 = calendar;
*Calendar calendar2 = (Calendar)calendar.clone();//创建了一个新的对象 用calendar2指向它
*
*calendar == calendar1 true
*calendar == calendar2 flase
*calendar.equals(calendar2) true
*克隆的calendar2 内容和calendar一样 但是是不同的对象
*
*如果一个类继承了Cloneable接口 就必须覆盖Object类中的clone()方法。
*public Object clone(){
* return super.clone();
*}
*
*House house = new House(1,175);
*House house1 = (House)house.clone();//返回的是Object类型的,但是实际类型是Houese 需要进行显式转换
*house和house1是内容相同的不同对象,Object类的clone()方法将原始对象的每个数据域复制给目标对象。
*如果数据域中的某个属性是基本类型,复制的就是它的值。
*如果数据域中的某个属性是 对象,复制是该对象的引用,不是对象的内容。(浅复制)
*
*为了克隆一个对象,该对象的类必须覆盖clone()方法并继承Cloneable接口。
* class Person implements Cloneable{
private int age;
private String name;
private Date birth;
public Person(int a,String n,Date b){
age=a;
name=n;
birth=b;
}
public static void main(String[] args){
Person kobe=new Person(33,"kobe",new Date());
Person kobe_bak=kobe.clone();
}
@Override
public Object clone(){
Person p=(Person)super.clone(); //返回的是Object类型的,但实际类型是Person类 需要进行显式转换
//Date类型的birth域是可变的,需要对其克隆,进行深拷贝
//Date类实现的克隆,直接调用即可
p.birth=this.birth.clone();
return p;
}
}
*
*包装类:
*许多Java的方法需要对象作为参数。例如,在ArrayList类中的add(object)方法向ArrayList中添加一个对象。因此要将基本数据类型并入对象或包装成对象,这种类就是包装类。
*这样可以利用通用程序设计。
*
*doubleValue(),floatValue(),intValue(),将对象转换为基本类型值。
*Integer I = new Integer(45);//这里也可以是"45" 字符串和数值都行 生成一个包装类对象I
*int i = I.intValue();//将对象I转换为基本数据类型值
*System.out.println(i);
*
*valueOf(String s)方法:
*Double doubleObject = Double.valueOf("12.4")创建一个对象,并初始化为指定字符串表示的值。
*
*包装类的静态方法parseInt,parseDouble等,直接将数值字符串转化为数值。默认为十进制。
*parseInt(String s,int radix),parseDouble(String s,int radix):
*Integer.parseInt("11",2) return 3
*Integer.parseInt("12",8) return 10
*Integer.parseInt("13",10) return 13
*
*Double类有parseDouble方法
*
*int j = Integer.parseInt("23");//parseInt返回数值
*System.out.println(j);
*
*Integer I1 = Integer.valueOf("12");//valueOf返回一个创建的对象
*
*都是静态方法。
*
*
*二进制IO和文本IO:
*
*对于文件内容的操作主要分为两大类.分别是:字符流,字节流.
*
* 其中,字符流有两个抽象类:Writer Reader (文本IO)
* 其对应子类FileWriter和FileReader可实现文件的读写操作
* BufferedWriter和BufferedReader能够提供缓冲区功能,用以提高效率.
*
* 同样,字节流也有两个抽象类:InputStream OutputStream (二进制IO)
* 其对应子类有FileInputStream和FileOutputStream实现文件读写
* BufferedInputStream和BufferedOutputStream提供缓冲区功能
*
* File对象封装文件或者路径的属性,但是不包含从\向文件读\写数据的方法,需要使用Java的IO类创建对象,这些对象包含从\向文件读\写数据的方法。
* File不属于文件流 , 只能代表一个文件或是目录的路径名而已。
*
* InputStream和OutputStream是二进制IO的根类:
* FileInputStream(file对象 或 String类型的filename)和FileOutputSream(file对象 或 String类型的filename)
*
* InputStream:FileInputSream,FilterInputStream,ObjectInputStream
* OutputStream:FileOutputSream,FilterOutputStream,ObjectOutputStream
* 它们都有read()或者write()方法。
*
* ObjectInputStream和ObjectOutputStream可以实现对对象的输入和输出。
* ObjectInputStream input = new ObjectInputStream(new FileInputSream("Object.dat"));
*可序列化:
* Java中ObjectInputStream 与 ObjectOutputStream这两个包装类可用于输入流中读取对象类数据和将对象类型的数据写入到底层输入流 。
* ObjectInputStream 与 ObjectOutputStream 类所读写的对象必须实现了 Serializable 接口。
* 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。
* 虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
* 当 通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。
* Foo myFoo = new Foo();
* myFoo .setWidth(37);
* myFoo.setHeight(70);
*
* FileOutputStream fs = new FileOutputStream("foo.ser");
* ObjectOutputStream os = new ObjectOutputStream(fs);
* os.writeObject(myFoo);
*
*
*BufferedXXXX 设立缓冲区,减少读写次数来提高输入和输出速度。
*
*当使用read()方法时,实际上是先读取buf中的数据,而不是直接对数据来源作读取。
*当buf中的数据不足时,BufferedInputStream才会再实现给定的InputStream对象的read()方法,从指定的装置中提取数据。
*
*当使用write()方法写入数据时实际上会先将数据写到buf中。
*当buf已满时才会实现给定的OutputStream对象的write()方法,将buf数据写到目的地,而不是每次都对目的地作写入的动作。
*
*BufferedReader data = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
*FileInputStream读入的是字节流,InputStreamReader将字节流转换为字符流 然后配合BufferedReader.
*BufferedReader类就是一个包装类,它可以包装字符流,将字符流放入缓存里,先把字符读到缓存里,到缓存满了或者你flush的时候,再读入内存,就是为了提供读的效率而设计的。
*BufferedReader由Reader类扩展而来,提供通用的缓冲方式文本读取,而且提供了很实用的readLine,读取分行文本很适合,BufferedReader是针对Reader的,不直接针对文件,也不是只针对文件读取。
*
*FileInputStream 类介绍:
* 以字节为单位的流处理。字节序列:二进制数据。与编码无关,不存在乱码问题。
* FileInputStream 类的主要方法有:
* Read(), read(byte[] b), read(byte[],int off,int len),available();
*
* FileInputStream 类与 FileReader 类的区别:
* 两个类的构造函数的形式和参数都是相同的,参数为 File 对象或者表示路径的 String ,它们到底有何区别呢?
* FileInputStream :以字节流方式读取;
* FileReader :把文件转换为字符流读入;
* InputStream提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。
* Reader类及其子类提供的字符流的读取char,inputStream及其子类提供字节流的读取byte,所以FileReader类是将文件按字符流的方式读取,FileInputStream则按字节流的方式读取文件;
* InputStreamReader可以将读如stream转换成字符流方式,是reader和stream之间的桥梁。
*
*
*
*
*
*
*/