面向对象三大特征

     1、封装:指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

      对一个类或对象实现良好的封装,可以实现以下目的:

      隐藏类的实现细节;

      让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里添加控制逻辑,限制对属性的不合理访问;

      可以进行数据检查,从而有利于保证对象信息的完整性;

      便于修改,提高代码的可维护性。

       为了实现良好的封装,需要从两个方面考虑:

        将对象的属性和实现细节隐藏起来,不允许外部直接访问;

        把方法暴露出来,让方法来操作或访问这些属性。

        而这两个方面都需要通过使用Java提供的访问控制符来实现。

      

      访问控制符用于控制一个类是否可以被其他类访问,对于局部变量而言,其作用域就是它所在的方法,不可能被其他类来访问,因此不能使用访问控制符。

      顶级类只能使用两个访问控制符修饰:public和默认,不能使用private和protected修饰,因为顶级类既不处于任何类的内部,因此private和protected对顶级类没有意义。使用public修饰顶级类,可以被所有类使用,如声明变量;不适用任何访问控制符的顶级类只能被同一个包中的所有类访问。

      注意:如果一个Java源文件中所有类都没有使用public修饰,则这个Java源文件的文件名可以是一切合法的文件名。但如果一个Java源文件里定义了一个public修饰的类,则这个源文件名必须与public类的类名相同。

public class Person {
 //将name和age属性用private修饰,进行封装
 private String name;
 private int age;
 public void setName(String name){//提供方法来操作name属性,并进行合理性校验
  if(name.length()>6||name.length()<2){
   System.out.println("您设置的人名不符合要求");
   return;
  }else{
   this.name=name;
  }
 }
 public String getName(){
  return this.name;
 }
 public void setAge(int age){
  if(age>100||age<0){
   System.out.println("您设置的年龄不合法");
   return;
  }else{
   this.age=age;
  }
 }
 public int getAge(){
  return this.age;
 }
}

    关于访问控制符的使用,存在如下几条基本原则:

     类里的绝大部分属性都应该使用private修饰,除了一些static修饰的、类似全局变量的属性,才可能考虑使用public修饰;除此之外,有些方法只是用于辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰;

      如果某个类主要用作其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法;

      希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造方法通过使用public修饰,暴露给其他类中创建该类的对象。

      构造器是特殊的方法,用于创建类的实例。Java类必须包含一个或一个以上的构造器。构造器的最大用处是在创建对象时执行初始化。

      2、继承:软件复用的重要手段。Java采用单继承。

      父类和子类的关系,是一种一般和特殊的关系。

       方法重写:又称方法覆盖,就是指子类包含与父类同名方法的现象。

       方法重写遵循“两同两小一大”规则,“两同”即方法名相同、形参列表相同,“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常比父类方法声明抛出的异常类更小或相等。“一大”指的是子类方法的访问权限应比父类方法更大或相等。

        注意:覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。

       例如:下面代码编译错误

        class BaseClass{

              public static void test(){...}

         }

         class SubClass extends BaseClass{

              public void test(){...}

          }

        当子类覆盖了父类方法后,子类对象将无法访问父类中被覆盖的方法,但可以在子类方法中调用父类中被覆盖的方法。调用可以使用super(被覆盖的是实例方法)或者父类类名(被覆盖方法是类方法)。

      如果父类方法具有private权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也无法重写该方法。如果子类中定义了一个与父类private方法具有相同的方法名,相同形参列表,相同返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。

      例如:下面代码是正确的

      class BaseClass{

             private void test(){...}   //test()是private访问权限,子类不可访问该方法

       }

       class SubClass extends BaseClass{

             public static void test(){...}  //此处并不是方法冲洗额,所以可以增加static 关键字

        }

 

     注意:Java程序创建某个类的对象时,系统会隐式创建该类父类的对象。只要有一个子类对象存在,则一定存在一个与之对应的父类对象。在子类方法中使用super引用时,super总是指向作为该方法调用者的子类对象所对应的父类对象。

     this:总是指向到调用该方法的对象

     super:总是指向this指向对象的父对象。

      this和super不能出现在static修饰的方法中,因为static修饰的方法属于类,该方法的调用者可能是一个类,而不是对象,也就不存在对应的父对象。

      方法重载(overload)主要发生在同一个类的多个同名方法之间;

      方法重写(override)主要发生在子类和父类的同名方法之间。

      当然,父类方法和子类方法之间也可能发生重载,因为子类会获得父类方法,如果子类定义了一个父类方法有相同方法名,但参数列表不同的方法,就会形成父类方法和子类方法重载。

      如果我们在某个方法中访问名为a的属性,但没有显式指定调用者,系统查找a的顺序为:

      查找该方法中是否有名为a的局部变量;如果没有则查找当前类中是否有包含名为a的属性;如果没有则查找a的直接父类中是否包含名为a的属性,依次上溯a的父类,直到java.lang.Object类,如果最终没有找到,则系统出现编译错误。

      子类不会获得父类的构造器,但有时子类构造器里需要调用父类构造器的初始化代码,可以使用super关键字。

       class Base{

             public double size;

             public String name;

             public Base(double size,String name){

                    this.size=size;

                    this.name=name;

             }

        }

        public class Sub extends Base{

               public String color;

               public Sub(double size,String name,String color){

                      super.(size,name);//通过super调用来调用父类构造器的初始化过程

                      this.color=color;

                }

                public static void main(String[] args){

                        Sub s=new Sub(5.6,"测试对象“,"红色");

                        System.out.println(s.size+"--"+s.name+"--"+s.color);

                 }

          }

        当调用子类构造器来初始化子类对象时,父类构造器总会在子类构造器之前执行,而且,执行父类构造器时,系统会再次上溯执行器父类的构造器,以此类推,创建任何java对象,最先执行的总是java.lang.Object类的构造器。

      例如:class Creature{

                        public Creature(){

                                System.out.println("Creature无参数的构造器");

                        }

                   }

                   class Animal extends Creature{

                          public Animal(String name){

                                 System.out.println("Animal带一个参数的构造器,该动物的name为”+name);

                           }

                           public Animal(String name,int age){

                                  this(name);//使用this调用同一个重载的构造器

                                  System.out.println("Animal带两个参数的构造器,其age为"+age);

                            }

                      }

                      public  class Wolf extends Animal{

                             public Wolf(){

                                  super("土狼",3);

                                  System.out.println("wolf无参数的构造器“);

                              }

                              public static void main(String[] args){

                                    new Wolf();

                               }

                        }

        运行结果:

        Creature无参数的构造器

        Animal带一个参数的构造器,该动物的name为土狼

        Animal带两个参数的构造器,其age为3

        Wolf无参数的构造器

        结论:创建任何对象总是从该类所继承树最顶层类的构造器开始执行,然后依次向下执行,最后才执行本类的构造器。如果某个父类通过this调用了同类中重载的构造器,就会依次执行此父类的多个构造器。

       3、多态

       Java引用变量有两个类型:一个是编译时类型,一个是运行时的类型,编译时的类型由声明该变量时使用的类型决定,运行时的类型由实际付给该变量的对象决定。如果编译时类型和运行时类型不一致,就会出现多态。

        第一个是通过方法重载实现;第二个是通过方法覆盖实现(子类覆盖父类方法)。
        第一种就是我们调用方法是不用区分参数类型,程序会自动执行相应方法,如: 加法运算,可以使int相加,可以是double相加,都是同一个方法名。
        第二种就是动态绑定,使用父类引用指向子类对象,再调用某一父类中的方法时,不同子类会表现出不同结果。 这样的作用就是扩展性极好,玩过网游的话应该知道 游戏中有不同的角色,它们都有一个父类,它们做相同动作时表现出来的效果就会不一样,比如跑,魔法师的跑跟战士的跑就不会一样,这就是俩者都覆盖了父类中的跑方法,各自有自己的现实,表现出来多态。  如果有一天你想再加个角色,只用再写一个类继承该父类,覆盖其中的跑方法就行了,其他代码不用怎么改,所以可维护性也很好

      注意:引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法。因此,编写Java代码时,引用变量只能调用声明该变量时所用类里的包含的方法。

      例如:Object p=new Person();

              定义了一个变量p,则这个p只能调用Object类的方法,而不能调用Person类里的方法。

      与方法不同,对象的属性不具备多态性。通过引用变量来访问器包含的实例属性时,系统总是试图访问它编译时类所定义的属性,而不是它运行时类所定义的属性。

      引用变量只能调用它编译时类型的方法 ,而不能调用它运行时类型的方法,但如果需要让这个引用变量来调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助类型转换运算符。

      类型转换运算符时小括号,即(type)variable,这种方法可以将variable变量转换成一个type类型的变量。

      进行强制类型转换时主要:

      基本类型之间的转换只能在数值类型之间进行,数值类型包括整数型、字符型和浮点型。但数值型不能喝布尔型进行转换;

       引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行类型转换。如果试图把一个父类实例转换子类类型,则必须这个对象实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则运行时引发ClassCastException异常。

     例如:

      public class TestConversion{

           public static void main(String[] args){

                  double d=13.4;

                   long l=(long)d;

                   System.out.println(l);

                   int in=5;

                    boolean b=(boolean) in;//编译时出错:试图把一个数值类型转换为boolean类型

                    Object obj="Hello";

                    String objStr=(String)obj;//obj变量的编译类型为Object,是String的父类,可以强制类型转换,而且obj变量实际上类型是String类型,所以运行时通过

                    System.out.println(objStr);

                     Object objPri=new Integer(5);

                      String str=(String)objPri;//objPri的编译类型为Object,是String类型的父类,可以强制转换,但是objPri实际上是Integer类型,所以下面代码运行时已发ClassCastException异常

                       }

              }

       考虑到强制类型转换时可能会出现异常,因此进行类型转换之前应先通过instanceof运算符来判断是否可以成功转换。

       例如:上面代码可改为

        if(objPri instanceof String)

        {

               String str=(String)objpri;

         }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值