ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

8.面向对象基础特征

8.面向对象基础特征 封装封装的好处封装之后才会形成真正的独立性程序之间耦合度降低。封装之后隐藏复杂的内部执行给使用者保留简单的操作。封装可以使程序提高复用性。封装之后对事物本身提高安全性(典型案例电视机和遥控器)。如何封装所有属性私有化使用private关键字进行修饰。对外提供简单的操作入口外部程序通过操作入口进行属性操作。操作方法中可编写业务逻辑代码。有static修饰的方法调用:类名.方法名没有static修饰的方法调用:引用.方法名publicclassUser{//属性私有化privateintage;publicintgetAge(){returnage;//根据java中的就近原则age 变量就是User类的age}publicvoidsetAge(intage){//age age; 根据java中的就近原则age 变量是SetAge的参数 age 是局部变量。如果setAge(int a) { age a; } 这里的age是User类的agethis.ageage;}}Super关键字super代表父类的引用用于访问父类的属性、方法、构造器。super可以访问父类的成员属性和方法但不能访问父类的private的成员.使用super(形参列表)访问父类构造器时必须放在子类构造器的第一行。使用细节当子类中有和父类中的成员重名时如果想调用父类的成员有权限访问的成员必须通过super。super访问遵循“就近原则”有些人认为super与this引用是类似的概念实际上这样比较并不太恰当。这是因为super不是一个对象的引用不能将super赋给另一个对象变量它只是一个让编译器调用父类方法或属性的特殊关键字。this和super的比较继承作用1.代码复用2.有了继承以后才能有“方法覆盖”和“多态”机制。语法格式[修饰符列表]class类名extends父类名{//类体}一些术语B 类继承 A类 A类父类、基类、超类、SuperClasss B类: 子类、派生类、SubClass使用细节子类继承了所有的属性和方法可以通过编译器debug模式查看到私有的属性和方法不能在子类直接访问。子类必须调用父类的构造器完成父类的初始化隐式的调用。创建子类对象时如果子类的构造器没有显式地调用父类的构造器则将自动地调用父类默认(没有参数)的构造器。如果父类没有无参构造器并且在子类的构造器中又没有显式地调用父类的其他构造器则Java编译器将报告错误那么这种情况就必须在子类的构造器中显式地调用super去指定使用父类的那个构造器完成父类的初始化工作。如果类中没有显示的继承类该类默认继承JavaSE库中的提供的java.lang.Object类。此类为java中所有类的基类。父类构造器的调用不限于直接父类将会一直往上追溯到Object类(顶级父类)。子类最多只能继承一个父类指直接继承即java中是单继承机制。继承的内存分析classPerson{privateStringname;privateintage;publicvoidsetName(Stringname){this.namename;}publicvoidsetAge(intage){this.ageage;}publicStringgetName(){returnname;}publicintgetAge(){returnage;}}classStudentextendsPerson{privateStringschool;publicStringgetSchool(){returnschool;}publicvoidsetSchool(Stringschool){this.schoolschool;}}publicclassTest{publicstaticvoidmain(Stringarg[]){StudentsnewStudent();s.setName(John);s.setAge(18);s.setSchool(SCH);System.out.println(s.getName());System.out.println(s.getAge());System.out.println(s.getSchool());}}子类没有的属性/方法会使用“就近原则”向上层查找如果最终没有查找到会编译报错如果查找到并且有访问权限就可以访问了。方法重写overwride什么时候进行方法重写在继承关系中父类中的方法已经无法满足子类中的业务需求这时需要将父类的方法在子类中进行重写。这个重写也叫方法覆盖。满足什么条件(代码满足的条件)进行方法重写必须发生在具有继承关系。子类需要重写的方法与父类被重写的方法要满足方法名、参数列表相同。重写时建议复制粘贴。子类需要重写的方法与父类被重写的方法要满足返回值类型相同或者这个类型的子类型。子类方法不能缩小父类方法的访问权限比如父类方法访问权限是protected, 子类就不能是private。抛出的异常不能更多只能更少。父类私有方法、构造方法、静态方法、final修饰的方法不存在覆盖。重写和重载比较多态对象(具体体现为方法)具有不同的形态称之为多态(polymorphism)。方法的重写和重载都是多态的体现。一个对象变量可以赋值多种实际类型的现象也是多态的体现。is-a关系is-a 关系 子类型关系 在 Java 中它通常通过继承或接口实现来体现。在 Java 里这种子类型关系可以由两种方式建立类继承extends接口实现implements接口继承接口interface B extends A使用is-a关系判断是否应该设计为继承关系Employeee;enewEmployee(...);// Employee object expectedenewManager(...);// OKManager can be used as well多态涉及到的概念编译类型编译类型就是你写代码的时候定义的类型。又编译器主要进行语法检测与编译器有关系。例如在父类指向子类对象的这个引用只能调用子类重写父类的方法子类私用的方法无法调用。因为这个引用是指向父类的编译器无法找到子类的私有方法。运行类型运行类型主要看堆内存中的指向。其实还是遵循“就近原则”向上转型upcasting子类型—父类型 又被称为自动类型转换。向下转型downcasting父类型 — 子类型 又被称为强制类型转换语法格式子类类型 引用名 (子类类型) 父类类型。只能强转父类的引用不能强转父类的对象。classAnimal{voideat(){}}classDogextendsAnimal{voidbark(){}}publicstaticvoidmain(String[]args){AnimalanewDog();// a 是 Animal 类型的引用但实际指向 Dog 对象。a只能调用eat方法因为引用是Animal编译器无法找到bark方法。Dogd(Dog)a;// 可以把父类引用强转为子类引用d.bark();// 成功调用 Dog 特有方法AnimalanewAnimal();// 实际对象就是 AnimalDogd(Dog)a;// 编译可以通过但运行时报 ClassCastException//安全做法if(ainstanceofDog){Dogd(Dog)a;d.bark();}}类型转换需要两个类型之间具有继承关系。多态语法机制A类和B类存在继承关系A-父类 B-子类。满足is-a规则A a new B();其中 a 引用的数据类型是A父类这里new出来的是A的子类B类型。所以他们进行了类型转换且是向上转型。java中允许这种语法父类型引用指向子类型对象。A a new B();静态绑定在编译阶段检查的类型a 表示的是父类引用只能使用父类的方法和属性这个编译的过程称之为静态绑定。只有静态绑定之后才会有后续的运行。字段在编译时根据引用类型决定classAnimal{StringnameAnimal;}classDogextendsAnimal{StringnameDog;}publicclassMain{publicstaticvoidmain(String[]args){AnimalanewDog();System.out.println(a.name);// 输出 Animal}}所以a.name访问的是Animal的name,字段是静态绑定动态绑定动态绑定Dynamic Binding也叫后期绑定、运行时绑定。程序在运行时根据对象的实际类型来决定调用哪个类的方法。在每个方法调用的过程中只要碰见父类和子类有同名且重写的方法都会使用动态绑定机制。packagecom.ploy_.dynamic_;/** * date:2022/6/3 21:46 * author:light **/publicclassDynamicBind{publicstaticvoidmain(String[]args){AanewB();//执行getI时候是调用的是 b对象的get方法这里遵循动态绑定机制。获取i时候不用遵循动态绑定机制。//执行getI可以理解为this.getI(); 此时这个this是指向b对象的因为是a对象调用sum方法。a对象指向new B()System.out.println(a.sum());//30System.out.println(a.sum1());//20 成员变量没有动态绑定}}classA{publicinti10;publicintsum(){returngetI()10;}publicintgetI(){returni;}publicintsum1(){returni10;}}classBextendsA{publicinti20;publicintgetI(){returni;}}不存在动态绑定的情况:成员变量、静态方法没有动态绑定静态方法属于类方法编译时就已经确定调用哪个类的方法。如果a(A的引用)想访问B类型自己的方法可以将a强制转换成B类型。这种转换称之为向下转型。向下转型也需要两个类型之间具有继承关系。如果不希望让一个方法具有多态性可以将它标记为final。面试题class Animal{ public void move(){ System.out.println(动物可以移动); } } class Dog extends Animal{ public void move(){ System.out.println(狗可以跑和走); } public void bark(){ System.out.println(狗可以吠叫); } } public class TestDog{ public static void main(String args[]){ Animal a new Animal(); Animal b new Dog(); a.move(); b.move(); b.bark(); //编译期Animal类型无法知道到bark方法可以强转Dog类型就可以访问了。 } }上面代码输出什么答案编译错误什么时候需要使用向下转型​ 父类型引用指向子类型对象当这个引用想调用的方法是子类型中特有的在父类当中不存在必须进行向下转型。向下转型中遇到的著名异常ClassCastException现在假设c类也继承A类,下面的代码在编译器通过运行期报错Ac1newB();Cc2(C)c1;因为在运行期c1指向的是B类型对象所以c1B类型引用无法强制转换成C类型对象引用因为两个类型之间不存在任何继承关系所以会发生著名异常java.lang.ClassCastException, 这种异常总是在“向下转型”时候发生。instanceof运算符可以避免java.lang.ClassCastException异常出现。instanceof语法格式引用instanceof数据类型名//返回值为boolean类型代码使用if(c1 instanceof C){ //如果c1指向的是C对象 C c2 (C)c1; //调用子类对象特有的方法 ... }else if(c1 instanceof B){//如果c1指向的是B对象 B c2 (B)c1; //调用子类对象特有的方法 ... }多态的作用降低程序耦合度提高程序拓展力面向对象编程核心思想面向抽象编程尽量不要使用具体编程。public class TestDt { public static void main(String[] args) { Master zhangsan new Master(); Cat tom new Cat(); zhangsan.feed(tom); Dog erha new Dog(); zhangsan.feed(erha); } } public class Master { public void feed(Pet c){ //这里的c使用的是一个抽象出来的宠物类而不是具体的宠物类 c.eat(); } } public class Pet { public void eat(){ } } public class Dog extends Pet{ public void eat() { System.out.println(小狗吃骨头); } } public class Cat extends Pet { public void eat(){ System.out.println(小猫正在吃鱼); } }多态的应用多态数组数组定义的类型为父类类型里面保存的实际元素类型为子类类型。Person[]personnewPerson[5];person[0]newPerson(xiaohua,20);person[1]newStudent(xiaoming,20,100d);person[2]newStudent(xiaozhang,20,60d);person[3]newTeacher(zhanglaoshi,18,1000d);person[4]newTeacher(lilaoshi,20,2000d);不能将一个超类的引用赋给子类变量。例如下面的赋值是非法的Teacher t Person[i]; // Error原因很清楚:不是所有的人都是老师。如果赋值成功m有可能引用了一个不是老师的Person对象当在后面调用老师的方法时就有可能发生运行时错误。在Java中子类数组的引用可以转换成超类数组的引用而不需要采用强制类型转换。例如下面是一个学生数组Student[] students new Student[10];将它转换成Person[]数组完全是合法的:Person[] persons students; // OK这样做肯定不会有问题请思考一下其中的缘由。毕竟如果Students[i]是一个Students也一定是一个Person。然而实际上将会发生一些令人惊讶的事情。要切记 Students和Persons引用的是同一个数组。现在看一下这条语句:Person[0] new Person(Harry, ...);编译器竟然接纳了这个赋值操作。但在这里Persons[0]与Students[0]引用的是同一个对象似乎我们把一个普通人擅自归入学生行列中了。这是一种很忌讳发生的情况当调用Student[0].readBook(“论语”)的时候将会导致调用一个不存在的实例域进而搅乱相邻存储空间的内容。为了确保不发生这类错误所有数组都要牢记创建它们的元素类型并负责监督仅将类型兼容的引用存储到数组中。例如使用new Students[10]创建的数组是一个学生数组。如果试图存储一个Person类型的引用就会引发自定义的异常。多态参数方法定义的形参类型为父类类型实参类型允许为子类类型。面试题public class Animal { public static void eat(){ System.out.println(Animal eat); } public void run(){ System.out.println(Animal Running); } } public class Dog extends Animal { public static void eat(){ System.out.println(Dog eat); } public void run(){ System.out.println(Dog Running); } } public class Test { public static void main(String[] args) { Animal animal new Animal(); Animal dog new Dog(); animal.run(); dog.run(); animal.eat(); dog.eat(); //编译的时候dog的类型是Animal类型所以调用Animal的静态方法eat() } }上面代码输出什么答案: Animal RunningDog RunningAnimal eatAnimal eat关于包Java允许使用包(package)将类组织起来使用包的主要原因是确保类名的唯一性。从编译器的角度来看嵌套的包之间没有任何关系。例如java.util包与java.util.jar包。包名命名规范公司域名倒序.项目名.模块名.功能名主要目的是让包名唯一。包名也是标识符遵守标识符规范全部小写。.隔开的包名分别对应一个目录。如何编译、运行使用package机制后编译java源文件在文件所在目录下编译javac xxx.java或者不再目录下编译javac path/xxx.javapath表示文件目录。运行java文件必须和顶级包名同级的目录下运行类名为: 包名类名如E:\javacode\j2se\oop8\srcdir...2022/03/1916:32DIRcom... E:\javacode\j2se\oop8\srcjava com.learn02.Test01 main begin c1 c2 c3 Test01 beginjava -d path/xxx.java编译和运行直接创建好编译后的包的目录字节码文件在最底层包目录里。如果没有在源文件中放置package语句这个源文件中的类就被放置在一个默认包(defaulf package)中。默认包是一个没有名字的包。import语句不仅可以导入类还增加了导入静态方法和静态域的功能。例如如果在源文件的顶部添加一条指令:importstaticjava.lang.System.*;//就可以使用System类的静态方法和静态域而不必加类名前缀:out.println(Goodbye, World!);//System.outexit(0);//System.exit访问控制权限这里的子类是指的是不同包的子类只要方法是私有的类的设计者就可以确信:它不会被外部的其他类操作调用可以将其删去。如果方法不是私有的就不能将其删去因为其他的代码很可能依赖它。publicclassObject_{publicstaticvoidmain(String[]args){AAaanewAA();aa.aas(aa);//aa.a 报错}}classAA{privateinta1;publicvoidaas(AAt){inta1t.a;//t变量为什么在这里能访问t对象的a属性System.out.println(t.a);}}面试题:类A1和类A2在同一包中类A2有个protected的方法testA2类A1不是类A2的子类或子类的子类类A1可以访问类A2的方法testA2。a. 正确b. 错误根据以下代码段下列说法中正确的是( )。publicclassParent{privatevoidm1(){}voidm2(){}protectedvoidm3(){}publicstaticvoidm4(){}}A. 子类中一定能够继承和覆盖Parent类的m1方法B. 子类中一定能够继承和覆盖Parent类的m2方法C. 子类中一定能够继承和覆盖Parent类的m3方法D. 子类中一定能够继承和覆盖Parent类的m4方法
返回列表