您的位置:首页 > 职场人生

黑马程序员——面向对象(2)

2012-07-20 01:42 232 查看
-----------
android培训java培训、java学习型技术博客、期待与您交流!
------------


 

十一、封装 encapsulation

1、将一类事物的特征和行为封装在一个类中,定义为成员变量和成员方法。类中的成员函数可以访问自己的成员变量。

2、为了实现良好的封装,可以用private关键字将成员变量私有化,对外提供

一个public型的set、get方法对成员变量赋值、获取成员变量的值。

例如:

  class Person {

       privateString name;

       privateint age;

       publicvoid setName(String name) {

           this.name= name;

}

       publicString getName() {

           returnname;

}

public void setAge(int age) {

    this.age = age;

}

public int getAge() {

return age;

}

}

十二、继承inherit

用extends关键字

1、特点

只支持单继承,不支持多继承。支持多重继承

   一个类可以有多个子类,只能有一个父类。

  加载类的时候先加载父类。

  一个了如果没有声明父类,则默认继承Object类。

2、子类对象创建的过程

   子类对象之所以能调用父类的方法,是因为在创建子类对象的时候,其内部创建了一个父类对象。调用子类方法时,JVM会现在子类中查找是否包含该方法,如果没有则找父类。

在调用子类构造函数的时候,一定会先使用”super(参数)”形式调用父类的构造函数创建父类对象。如果是调用父类无参的构造函数,可以不写super(),如果调用父类有参数的构造函数,必须写 super(参数)。

构造函数的第一条语句要么是this,要么是super,二者选其一。如果没写,则默认是super()。

3、向上转型

  子类对象可以当父类对象使用。 Fruit f = new Apple();

  如果一个方法要求传入一个父类类型的对象,我们也可以传入一个子类对象。

  子类对象当做父类来用时,不能调用子类特有的成员。编译时语法检查报错。

  子类对象当做父类来用时,用父类类型的变量调用方法时,先找子类的方法(java的动态分配机制);如果调用变量则找父类的。

   创建的是哪个类的对象,调用方法时就先在哪个类中找(与变量类型无关)。

4、强制类型装换

  将向上转型成父类的子类对象强转回子类对象。无论类型是否匹配,编译时都不会报错。语法:“(父类类型)对象的引用变量;”

  强转之前进行类型判断:if(f instanceof Apple){ Apple a= (Apple) f;}

5、子类重写(Override)父类的方法

  重写父类方法时,返回值类型、参数列表必须完全一致。

  重写父类方法时,不能定义更低的访问权限。

public > protected > default > private

  重写父类方法时,不能抛出更多的异常。

  一个方法在重写父类方法后,若要调用被覆盖的方法,使用“super.方法名”。

  可以在方法前加@Override检查重写是否成功。

十三、多态polymorphism

一段代码可以运行出多种形态。用父类的引用指向多个子类的对象。

多态的前提是类与类之间有关系,要么有继承、要么实现,通常还要实现方法的覆盖。

将函数的形参定义为父类类型,所有的子类都可以传入,这时我们可以将一个子类对象作为实参传递过去,此时方法定义的形参为父类,在方法中使用父类变量调用方法时,其实是调用子类的方法(在把子类当做父类来用时,使用父类变量访问方法,访问的是子类的方法,因为虚拟机会找到变量引用的地址,根据这个地址来访问方法,这叫动态分配)——向后兼容。可以提高代码的复用性。

(1)定义一个类,其中的方法形参为父类     

class Juicer {

       publicvoid run(Fruit fruit) {  

           fruit.squeeze();

}

}

(2)定义一个父类 

class Fruit {

 public void squeeze() {}

}

(3)子类重写父类的方法

class Apple extends Fruit {

public voidsqueeze() {

 System.out.println(“榨出一杯苹果汁”)

}

}

(4)主函数中调用时传入子类对象,调用子类的方法:

 new Juicer().run(newApple());

十四、抽象类

1、概念

用abstract class修饰的类就是抽象类。

抽象类中可以定义抽象方法(用abstract修饰的方法)。

2、应用情景

在定义类时如果多个类有相同的方法,那么就把这些方法抽取到父类中定义。完全相同的方法直接在父类中定义并实现,如果只有方法签名一样而实现代码不同,那么就可以在父类中定义抽象方法。这样在看到父类的时候就知道子类都有什么功能了。

3、注意

(1)有抽象方法的类必须声明为抽象类,抽象类不可以用new创建对象,因为调用抽象方法没意义,没有实现,不能运行;

(2)抽象类可以没有抽象方法,目的就是为了不能创建对象;

(3)抽象类不能创建对象,就是用来表示子类有哪些方法的,是为了支持多态(将子类对象当做父类来用,可以调到子类的方法);

(4)子类继承抽象类是必须重写所有的抽象方法。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类;

(5)抽象类中的抽象方法要被使用,必须由子类重写起所有的抽象方法,建立子类对象调用。

4、抽象类和一般类比较

(1)抽象类和一般类没有太大区别:如何描述事物,就如何描述事物,只不过该事物出现了一些不确定的部分。也就是该事物的功能需要明确出现,但是无法
4000
定义主体,通过抽象方法来表示;

(2)抽象类比一般类多了一个抽象方法;

(3)抽象类不可以实例化。

十五、接口interface

****

抽象类

接口

可以有抽象方法,可以有不抽象的方法

所有的方法都是抽象的

子类用extends继承

子类用implements实现

成员方法没有默认修饰符

成员方法默认public abstract修饰

成员变量没有默认修饰符

成员变量默认是public static  final

抽象类可以继承(extends)抽象类,抽象类不能继承接口

接口可以继承(extends)接口,

接口不能继承抽象类

一个类智能继承一个抽象类

一个类可以实现多个接口

能用接口的时候就不用抽象类。原因:接口不占用继承的位置,一个类可以同时继承一个类实现多个接口(extends在implements之前)。、

如果需要定义不抽象的方法则只能使用抽象类。

十六、内部类

1、类中的内部类:

(1)概念

在类的级别下定义的类,内部类也是外部类的一个成员。

内部类必须先创建外部对象才能创建对象。

语法:外部类名.内部类名 变量名=new 外部类名().new 内部类名();

(2)内部类访问外部类的成员(私有的和公有的)

     语法:外部类名.this.成员名——其中,“外部类名.this”代表调用当前方法的内部类对象的外部类对象

方法中、内部类中、外部类中的变量重名时,直接使用变量名访问的是方法

中的变量(不会自动初始化,使用前需初始化),使用“this.变量名”访问的是内部类的成员变量,使用“外部类名.this.成员名”访问的是外部类的成员变量。

(3)使用情景

  在定义一个类的时候,如果需要访问当前类的私有成员,则可定义成当前类的内部类。

(4)注意事项

a.外部类不能访问内部类的成员。原因:一、作用域;二、对象的生命周期(创建外部类对象的时候不一定有内部类对象,创建内部类对象的时候一定有外部类对象)。

b.内部类生成的.class文件名是“外部类名$内部类名.class”

2、方法中的内部类

(1)只能在当前方法中使用。访问外部类的成员变量:外部类名.this.内部类名。

   使用方法中的内部类和普通的内中的内部类相同。

(2)应用情景:

  如果一个类只在某个方法中使用,就可以定义为方法中的内部类。

(3)注意事项:

a.方法中的内部类不能访问方法中定义的局部变量,除非将变量声明为final型的。原因:方法中定义的局部变量在方法运行结束后就被销毁,生命周期结束;而对象则在方法运行结束后不一定销毁,只有没有引用指向时才结束。

 因此,内部类访问方法中的局部变量时,必须把变量声明为final型的。

 b.编译生成的.class文件名为“外部类名$编号 内部类名.class”

——匿名内部类:方法中内部类的一种。可以继承一个类,也可以实现接口。

语法:new 父类名/接口名(){ 类定义 } ——声明指定类的子类,用这个子类创建一个对象

编译生成的.class文件名为“外部类名$编号.class”

如果一个类只使用一次,就可以定义为匿名内部类。

3、静态内部类

  在非静态的内部类中不能声明静态的成员。

(1)特点

 a.不用创建外部类对象就能直接创建内部类对象,可以定义静态成员。

语法:外部类名.内部类名 变量名 = new 外部类名.内部类名();

  b.不能访问外部类的非静态成员

  c.在静态内部类中访问外部类的静态成员:外部类名.外部静态成员名

  d.不能使用“外部类名.this”

(2)应用场景:

   如果定义一个类时,不需要访问外部类的非静态成员,就可以定义为静态内部类。如果需要定义静态成员,只能声明为静态的。

十七、包 package

总结:写所有的类都要带包,第一条语句写package;

写public类时和文件名同名;

导入的时候用类名:import 包名.类名

编译时用 –d;

运行时带包名。

1、定义包

package xxx;——必须为java源文件的第一条语句。包名全部小写,用分号隔开,通常是“公司域名.项目名.功能层”.

如果一个类中没有package语句,这个类为缺省无包名。

2、编译执行带包的java文件

编译:javac -d <目录> 源文件名.java  例如:javac –d . MyJava.java

  执行:java 包名.类名   例如:java cn.itcast.MyJava

3、导入不同包中的类:

  被导入的类必须是public型的,否则不能在不同的包中使用。公有类的类名必须和文件名相同。一个文件中只能写一个公有类。

(1)import 包名.*;——在用类名访问指定的类时,会现在当前包中查找是否有这个类,如果没有才会到导入的包中查找。

(2)import 包名.类名;——在用类名访问指定的类的时候,无论当前包中是否有这个类,都会找指定的包。

(3)全限定名:包名.类名——如果需要访问不同包中两个同名的类,那么在写类名的时候可以直接写全限定名。

4、有包的类和无包的类的相互访问

  有包的类可以访问有包的类,无包的类可以访问无包的类。

  无包的类可以访问有包的类,直接导入即可。

  有包的类不可以访问无包的类。直接使用类名是在当前的包中查找,而当前包中没有无包的类,且无法导入包。

5、jar文件

(1)概念:

java文件的压缩格式。通常将软件中若干个.class文件打成一个jar文件。

(2)打jar包:jar cvf <归档文件名>文件名1 文件名2 文件(夹)名3;

    更新jar包:jar uvf <归档文件名>文件名;

    例如:jar cvfclasses.jar A.class B.class;

           jar uvf classes.jar C;

(3)使用jar包中的类:

    将jar包的绝对路径配到classpath中。

    例如:set classpath=E:\classes.jar;.(如果不加. jvm只找jar包,不找当前目录)。

(4)运行jar包:

    在META-INF文件夹中找到MINIFEST.MF文件;

    在文件中指定Main-Class属性,指定要运行的类;

例如:Main-Class: cn.itcast.MyJava;

运行命令:java –jarjar文件名   例如:java –jar MyJava.jar;

十八、异常

1、概念:程序运行过程中抛出的一些错误。

2、分类:见下图

3、抛出异常

  用throw关键字抛出异常对象。throw new RuntimeException(“出错了”);

程序中一旦出现异常,下面的代码就不会再执行了。

  如果抛出的是RuntimeException,那么调用当前方法的代码可以不处理;否则,调用处必须处理异常。

 

Throwable:所有错误或异常的超类

错误(error):由java虚拟机生成并抛出

异常(Exception)

编译时异常:Exception中除了运行时异常的部分,在编译之前必须对代码中的异常进行处理

运行时异常(RuntimeException的子类):在编译之前可以不进行处理。如:空指针异常、算数异常等

 

 

 

 

 

 

 

 

4、处理异常

(1)在当前方法签名处用throws声明,表明当前方法也有可能抛出异常;

(2)try {

可能出现异常的代码

} catch(Exception e){

e.printStackTrace();     // 打印异常信息

}

5、Finally代码块

  和try配合使用,只要执行了try中的代码,无论如何都会执行finally中的代码,除非使用System.exit(0)推出java虚拟机,这样做不会执行finally。

  try{ 可能出现异常的代码 } Finally{ 无论如何都会执行的代码 }

6、自定义异常

如果要定义一个编译时异常, 可以定义类继承Exception,然后创建对象, 用throw关键字抛出即可。

如果要定义一个运行时异常, 那么就定义类继承RuntimeException。

例1:class MyExceptionextends Exception{ }

例2:class MyExceptionextends RuntimeException{

       publicMyException(){ }

//若不定义有参数的构造函数,抛出异常时不能传入异常信息。

throw    new Myexception(“出错”);

 

       publicMyException(String msg) { 

            super(msg);

}

}                         

调用父类的构造函数,打印出异常信息:出错

7、注意:

(1)子类重写父类的方法时不能抛出更多的异常;

(2)处理多个异常的方法:

a.throws 异常1,异常2

b.try{可能出现异常的代码} catch(ClassNotFoundException e){ … }

       只会执行一个catch     catch(FileNotFoundException e){ …}

                           catch( Exception e ){ …}

                      父类异常,不要写在子类上面,否则会报错

3、try与finally嵌套

  try{

可能出现异常的代码

} Finally{ try{ 可能出现异常的代码 }

Finally{ 无论如何都会执行的代码 }

}

十九、访问权限

 

private

default

protected

Public

同一个类中









同个包中的类

 







子类

 

 





其他包中的类

 

 

 



 

二十、Object类:

Object类是java中所有类的父类。定义一个类,若没有继承任何类,默认继承object类。

1、finalize方法

Object类定义了一个finalize()方法,所以的类都会有这个方法。任何对象在成为垃圾被销毁之前,会自动调用finalize()方法。

虚拟机中的垃圾对象不会马上被回收。当对象堆积到一定程度时才会被回收。如果我们希望清空所有的垃圾,就可以调用System.gc();

System.gc(); // 清理虚拟机中的所有垃圾对象,异步方法(这个方法会开启一个新的线程,即使没有执行完下面的代码也能继续执行)。销毁垃圾需要时间。

Thread.sleep(3000);  // 程序休眠3000毫秒

Object类中的finalize方法是空的,什么代码都没有。我们在定义个一个类的时候,希望这个类的对象在被销毁之前执行某段代码,就可以在类中重写finalize方法。

例如:

@Override

protected void finalize() {

       System.out.println(“对象被回收”);

}

2、toString方法:

将对象转为字符串表示形式。Object类中toString方法返回“类名@地址“。

  调用print或println方法打印对象时,方法内部会自动调用toString方法,打印出这个对象的字符串形式。所以System.out.println(newPerson().toString)与System.out.println(new Person())的结果一样,都是“类名@地址”。但是由于String类重写了toString方法,所以System.out.println(new String(“abc”))打印出的是abc。

  当希望调用toString方法返回成员变量的值,而不是返回地址时,就可以重写该方法。

例如:

@Override

public String toString {

        return name + “,” + age;
}

3、equals方法:

比较两个对象的是否相同。Object类中的equals默认比较的是对象的地址。

  由于String类重写了Object类的equals方法,比较的是两个字符串的内容是否完全一样。比较两个字符串的内容是否相同时,应用equals方法。如果使用 == 则比较的是地址。

  在定义类的时候,如果希望这个类的对象可以按照属性比较,而不是比较对象的地址,那么就应该重写equals方法,在方法内比较所有的属性。

   例如:

   @Override

   public boolean equals(Object obj) {

       if(this== obj)     // 如果地址相同,返回true,为了提高效率。

           returntrue;        

       if(!(objinstanceof Person)) // 如果obj不是Person类型返回false

           returnfalse;        

                                  

Personother = (Person) obj;  //由于要使用子类特有的成员,将object强转回Person

if(this.name== null) {    // 如果自己的名字为空,对方名字不

是空,返回false,避免空指针异常

           if(other.name!= null)

              returnflase;

} else if(!this.name.equals(other.name))

 //调用String类的equals()方法

           returnfalse;

       if(this.age!= other.age)

           returnflase;

       returntrue;

}

二十一、文档注释:

生成文档注释的类必须是public型的,因此文件名和类名也一样。

生成文档注释的命令: javadoc –d (目录) –version –author (源文件)

1、类的注释:写在定义类之前

/**

* 这是一个用来描述人的类

* @author  作者姓名  ————作者信息

* @version 版本信息  ————版本信息

*/

2、方法的注释:写在定义方法之前

/*

* @param 参数名   ————参数信息说明

* @return ————返回值说明

*/

二十二、main()方法的语法

public 代表当前方法是公用的,所有的类都可以调到,为了让虚拟机可以调用。

static 代表当前方法时静态的,不用创建对象就可调用,方便虚拟机可以直接调用到。

void 代表该方法没有返回值,这个方法时虚拟机调用的,没有返回值。

main方法的名字, 启动虚拟机后, 虚拟机会自动找这个方法。

 (String [] args) 参数列表, 启动程序时可以传入若干字符串, 虚拟机在调用main函数的时候会传进来。

二十三、创建对象时内存中的工作顺序

Person p = new Person();

(1)在栈内存中开辟一块空间,存放变量p;

(2)jvm会读取指定路径下的person.class文件,并加载进内存。

如果有直接的父类先加载父类;

(类在第一次使用的时候被加载:如创建对象,使用静态变量,使用静态方法。)

(3)将类中的静态成员放入静态区域,默认初始化(引用数据类型为null,基本数据类型为0),执行静态成员代码块。

(4)调用构造函数创建对象,在堆内存中开辟空间,分配地址给创建的对象new Person(),将普通成员变量进行默认初始化(引用数据类型为null,基本数据类型为0);

(如果构造方法中显式或隐式地用super调用父类的构造函数,则先执行父类的构造函数。)

(5)在类中,成员代码块与成员变量赋值语句之前按照从上到下的顺序执行,哪个在前面就先执行哪个;

(6)执行该类的构造函数中的代码;

(7)将变量p指向对象的地址。

 

-----------
android培训java培训、java学习型技术博客、期待与您交流!
------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息