JAVA基础第二章 面向对象进阶
2.1封装与getter、setter方法
1.读写是两个操作,读即取值、访问值,写即赋值。
2.面向对象的三大特征:封装、继承、多态
(1)封装--一个程序肯定会定义很多类,那么为了保证各个类之间数据的安全,我们需要将类的某些属性(或还有某些方法)隐藏起来。封装就是指将某类的部分属性、部分方法隐藏在该类类内,使得在其它类里只能通过受保护的方法才能读写到这些成员。
注:这种隐藏并非指“真的看不见”,指“在类外不允许用‘对象名.属性’直接读写,而是必须通过该类提供的公共方法才能实现对隐藏信息的操作和访问”
例如:
- class Person{
- private int age;
- public int getAge (){
- return age;
- }
- public void setAge (int a){
- age=a;
- }
- }
(2)继承--父类和子类之间共享属性和方法。继承的好处:
①更好地进行抽象与分类
②实现代码重用,提高开发效率和可维护性
(3)多态--几个类分别各自重写了某方法后,当不同类的对象去调这个名字的方法时,产生的效果也是完全不同的。例如:
- foo( Person p ){
- p.sayHello ();
- }
- foo( new Student() );
- foo( new Teacher() );
3.实际业务中,对于属性的读写问题往往还有一些规则和范围,为了避免“对属性的随意访问和对属性不合理赋值”两个问题,读写属性时往往需要两次的判断:一是判断当前操作者是否可以读写(如验证密码)、二是判断新修改是否符合要求(如必须六位)。一般,会通过public的有参的getter和setter方法内的条件判断语句来实现。
例.在test class的main方法执行:
- Person P = new Person();
- P.age = -1000;
我们发现:对于public的属性,在包外也可直接用“对象.属性”实现对属性的读写。而且这种操作还成功地完全绕开了getter和setter方法,而如果项目又需要在getter和setter方法内设置是否允许读写的条件判断(如验证密码)、设置新修改值范围是否符合规定的条件判断(如必须六位),那么这种判断就都会失效。故为了属性的安全性和修改的合规性,需要想办法使“对象.属性”不能直接读写属性、需在修改时加入修改是否合规的判断。即使用封装。
4.总结封装的步骤:
①将属性定义为private属性(本类类外无法通过对象名.属性读写)
②创建public的getter和setter方法—用于在类外读写private属性
注:经过前两步,读写private属性只能通过public的getter和setter方法
③如果需要做是否拥有读写权限的判断、新修改是否合规的判断,则再在方法体中加入条件判断语句(需传参!以判断是否有读写权限、新修改是否合规)
5.封装的优点:
(1)属性用private修饰,使其被封装和隐藏,外部类不能随意读写。
(2)提供方法来存取对象的属性,在方法中可以对给定的参数的合法性进行检验。
(3)方法可以用来给出计算后的值。
(4)方法可以完成其他必要的工作(如清理资源、设定状态等等)。
(5)只提供getXXXX方法,而不提供setXXXX方法,可以保证属性是只读的。
7.getter、setter方法就是为封装而设计的,否则便没有了意义。
8.使用private修饰的属性、方法只能在本类中用“对象名.”直接访问、用“对象名.”直接调用。实际开发中,一般把属性都定义为私有属性,一般把getter、setter方法都定义为公共方法,其它方法根据有无能在类外直接访问的需要来决定是定义为公有还是私有。
2.2私有属性
1.对于public的/默认访问权限的属性,只要是同一个包内的类,就可随意用“对象名.属性”直接读写。对于private修饰的属性,仅本类类内可用“对象名/this.属性”读写。
2.以修改狗对象的年龄为例,pojo实体包内的代码:
- public class Dog {
- private int age; //私有属性, 类外无法由“对象名.属性”直接读写
- //public的有参getter方法,只有用户名和密码正确才可“读”
- public int getAge(String uname,String upwd) {
- if(uname=="aaa" && upwd=="123") {
- return age;
- }else {
- return 0;
- }
- }
- //public的有参setter方法,只有用户名、密码、新修改值正确才可“写”
- public void setAge(String uname,String upwd,int age) {
- if(uname=="aaa" && upwd=="123") {
- if(age>0 && age<=20) {
- this.age = age;
- }else {
- System.out.println("新年龄不符规范!");
- }
- }else {
- System.out.println("用户名和密码错误");
- }
- }
- }
3. 以修改狗对象的年龄为例,test检测包里的代码:
- public class Test {
- public static void main(String[] args) {
- Dog d=new Dog(); //对象实例化
- System.out.println(d.getAge("aaa", "123"));
- //只有用户名、密码正确才可读取private属性
- d.setAge("aaa", "123",15);
- //只有用户名、密码正确且新修改合规才可写入private属性
- System.out.println(d.getAge("aaa", "123"));
- //检验private属性的新值是否已被写入
- }
- }
4.我们发现:在类外调用getter、setter方法的过程,就是在本类类内用“对象名/this.属性”直接读写属性的过程。对于private属性,getter、setter方法起到了中介作用,因类外不能用“对象名/this.属性”直接读写属性,所以就需要一个public方法来满足在类外读写属性的需求,即充当中介。
2.3私有方法
1. public的方法,只要在同一个项目内,就可用“对象名.方法名(参数列表)”直接调用。private修饰的方法,仅本类类内可用“对象名.方法名(参数列表)”调用。
2. 如果只想让某方法能在类内的某时直接调用,而不想让该方法可在类外被直接调用,那就可用private去修饰该方法。
2. 以调用eat()、playFeiPan()为例,pojo实体包内的代码:
- public class Dog {
- private String name; //私有属性
- private int age;
- //public的eat()方法,用打印输出语句模拟eat的过程
- public void eat(int foodWeight) {
- System.out.println("狗在吃狗粮!");
- System.out.println("三分钟后.....");
- if(foodWeight<=10) {
- System.out.println("没吃饱!!");
- }
- else {
- System.out.println("吃饱喝足了!");
- this.playFeiPan();
- }
- }
- //private的playFeiPan(),类外无法调用
- //定义为私有是为了表达:不能直接让狗去接飞盘,只有狗吃饱喝足了才能接
- //即这个方法只有在public的eat()方法内满足一定的条件后才能调用
- private void playFeiPan() {
- System.out.println("狗狗开始玩接飞盘游戏!");
- }
- }
3. 以调用eat()、playFeiPan()为例,test检测包里的代码:
- public class Test {
- public static void main(String[] args) {
- Dog d=new Dog();
- //由于playFeiPan()方法只有在public的eat()方法内满足一定条件后才能调用
- //下面分别演示:未调用和调用
- d.eat(5);//通过public的方法在类外未能间接调用了private的方法
- d.eat(15);//通过public的方法在类外成功间接调用了private的方法
- }
- }
2.4实例-银行卡
1. 描述. 银行卡类中,属性设为私有,并给所有属性设置正确的get和set方法:
①在卡号的set方法里判断输入的卡号,必须为19位; ②在密码的set方法里判断输入的密码,必须为6位;③在余额的set方法里判断输入的初始余额,必须大于0 ; ④定义一个存款方法,并且在存款额度大于100000时调用私有方法:给余额增加千分之一
2.实际开发中,只要没特殊要求,属性都定义为private
3. pojo实体包内的代码:
- public class Card {
- //属性设为私有
- private String cnum;
- private String cpwd;
- private double cbanlance;
- //右键source里的自动创建getter、setter方法后,在对方法进行修改
- public String getCnum() {
- return cnum;
- }
- public void setCnum(String cnum) {
- //String也有length属性
- if(cnum.length()==19) {
- System.out.println("卡号有效!");
- this.cnum = cnum;
- }else {
- System.out.println("卡号必须是19位!");
- }
- }
- public String getCpwd() {
- return cpwd;
- }
- public void setCpwd(String cpwd) {
- if(cpwd.length()==6) {
- System.out.println("密码有效!");
- this.cpwd = cpwd;
- }else {
- System.out.println("密码必须是6位!");
- }
- }
- public double getCbanlance() {
- return cbanlance;
- }
- public void setCbanlance(double cbanlance) {
- if(cbanlance>=0) {
- System.out.println("初始余额设置成功!!");
- this.cbanlance = cbanlance;
- }else {
- System.out.println("初始余额必须大于等于0!!");
- }
- }
- //普通public方法,存款。只有存款金额大于0,才能存
- public void cun(int money) {
- if(money>0) {
- this.cbanlance=this.cbanlance+money;
- //只有本次的新存金额大于10万时才调用add方法,且add方法的基数是存款后的总余额
- if(money>=100000) {
- this.add();
- }
- System.out.println("存款成功,存入"+money+"元,奖励:"+this.cbanlance*0.001+"元。"+this.cbanlance+"元");
- }else {
- System.out.println("存款金额错误!");
- }
- }
- //新存入金额大于10万时,就奖励一些钱,即此时才调用这个奖励的方法,应为private
- private void add() {
- System.out.println("大额存款,奖励"+this.cbanlance*0.001+"元");
- this.cbanlance=this.cbanlance*(1+0.001);
- }
- }
4. test检测包里的代码:
- public class Test {
- public static void main(String[] args) {
- //对Card实例化并初始化属性值
- Card c=new Card();
- c.setCnum("123457890123456789");
- c.setCpwd("123qaz");
- c.setCbanlance(1000);//设置初始余额
- //调用存款方法,且新存入金额大于10万时会间接调用奖励方法
- c.cun(500000);
- }
- }
2.5浅识继承
1.万事万物都是对象,而每个对象又分别对应着其所属的类。但这些类之间也并非相互独立(如桌子类与课桌类、台球桌类、乒乓球桌类间就存在一种类与类之间的包含与被包含的关系)。在编程中,我们用继承来描述这种类与类间的包含与被包含的关系,称作父类/超类(superclass)与子类/基类/派生子类。
(1)单根继承原则:一个父类可有多个子类,但一个子类只能直接继承一个父类(由于这个子类的父类可能也有父类,所以对于某一个子类,其能够直接继承的父类只有一个,但其间接继承的父类可能有多个)
(2)子类从父类处,所继承的是父类类内所定义的所有属性和所有方法。当然,子类能否访问父类的私有成员又是另一回事了。
2.子类与父类是is-a的关系,即“子类is a 父类”。例如“由于Dog is a Pet所以狗类是宠物类的子类”、“由于Penguin is a Pet所以企鹅类是宠物类的子类”、“由于Cat is a Pet所以猫类是宠物类的子类”
3.同一父类的各子类间,肯定有一些相同的属性、方法。如果每个子类都去把各自的属性、方法逐个地定义,那肯定会造成代码的冗余。此时,就采取“继承”来优化设计:即抽取相同的属性、方法放到父类中去定义,只在各子类中定义特有的属性、方法。这样就避免了各子类对某些共同属性、方法的重复定义,而且还便于修改。
4.被继承却不能被子类直接访问的父类成员:
①private成员;②子类与父类不在同一包时,使用默认访问权限的成员
注:子类与父类不在同一包时,若需访问默认访问权限的成员,则需导包
5.关于父类构造方法的继承
例:父类Person有一个构造方法Person(String name, int age),不能说子类Student也自动有一个构造方法Student(String name, int age)。但子类在构造方法的第一句,可以用super来调用父类的构造方法。使用时,super()必须放在第一句。
- Student(String name, int age, String school ){
- super( name, age );
- this.school= school;
- }
注:能这样操作是有要求的:
①必须在子类构造方法的第一句去调用
②无论是父类的有参构造方法,还是父类的无参构造方法,只要父类显式定义了,就能通过这种方法调用。
6. 继承的好处/意义:
(1)子类继承父类的属性、方法(包括私有成员,当然是否有访问权限是另一件事),但避免了重复定义
(2)可以修改父类的属性、覆盖父类的方法(override,本质是方法的重写)
(3)可以添加新的属性、方法,即各子类特有的属性、方法
(4)可以提高程序的抽象程度
(5)实现代码重用,提高开发效率和可维护性
2.6使用继承&方法重写
1.使用继承的步骤与实例
强调:除继承父类的属性、方法以外,子类还可定义自己特有的属性、方法。有两种情况:
①完全新定义一些新属性、新方法
②对于已经在父类定义过的方法,如果子类的这个方法有其特殊之处,则用方法重写覆盖它
注:当子类特有的属性、方法与父类重名时,优先调用子类的【见方法重写】例:
(1)定义父类
- public class Pet {
- //父类:定义公共的属性和方法,父类的属性和方法不要设为私有
- String nick;
- int age;
- String color;
- String type="国产宠物";
- public void eat() {
- System.out.println("宠物在吃!");
- }
- public void run() {
- System.out.println("宠物在跑!");
- }
- }
(2)定义子类1
- public class Dog extends Pet{
- }
(3)定义子类2
- public class Cat extends Pet {
- }
(4)用含main方法的.java文件测试
- public class Test {
- public static void main(String[] args) {
- Dog d=new Dog();
- Cat c=new Cat();
- d.eat();//Dog类已经继承了父类Pet类内定义的eat方法
- c.run();//Cat类已经继承了父类Pet类内定义的run方法
- System.out.println(d.type);//Dog类已经继承了父类Pet类内定义的type属性
- }
- }
2.方法重写
(1)子类除继承父类的属性、方法外,还可定义自己特有的属性、方法。当子类特有的属性、方法与父类的公共属性、方法重名时,优先调用子类的。
(2)方法重写(覆写:通过在子类中定义与父类中某方法同名的方法,实现对父类某方法的重写。
①方法的重载(overload),指同一个类内,参数列表不同的同名方法。
②方法的重写(override),指子类重写父类的方法,除方法名一样外,其它的都可以不一样。
(3)意义:同一父类的各子类都有某一种方法,但各子类在调用这种方法时又各具特点,所以:一方面为体现这种方法是各子类都有的,我们需在父类定义一个空方法;另一方面,为体现不同子类在执行该方法时独具特点,我们需要在各子类分别进行有针对性的定义。
(4)多态:
①通过方法重载可以实现多态,重载是一个类中多态性的一种表现。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法。
②通过方法重写也可以实现多态,重写是不同类间多态性的主要机制。重写主要用于子类和父类之间,在父类中定义了一个方法,同时在子类中对这个方法进行重写,实现了子类行为的特殊化。
例:Pet类内定义eat、run两个公共方法;对于Pet类的子类Dog类、Penguin类,在eat、run上各有特色(因狗有狗的吃法、企鹅有企鹅的吃法,狗有狗的跑法、企鹅有企鹅的跑法)
2.7父类对象与子类对象的转换【这是理解多态的基础】
1.理解这个问题有个前提,就是弄清引用变量与对象之间的关系
(1)什么是某个类的对象?对象=“new 类名( )”,所以对象本身是没有名字的
(2)虽然对象本身没有名字,但我们需要操作对象,故我们需要标识符去区分不同的对象。因此,规定用该类类型的一个变量去接收这个对象。只不过因为“new 类名( )”的返回值是引用,所以这个接收对象的变量是引用变量,这个引用变量接受的值是引用值。
(3)因此,衍生出了对象的定义格式:类名 引用变量名 = new 类名( );
(4)最终,因为对象没有名字、一个引用变量只能接收一个对象的引用,所以我们就直接称这个引用变量名为对象名。而且还硬性规定了:对所创对象的所有操作都要去操作接收其引用引用变量。
2.子夫类对象的转换中所提及的对象指的是“new 类名( )”这种实质的真正的对象,并不是指引用变量。
3.存在继承关系的父类对象和子类对象之间也可以在一定条件下相互转换着使用:
(1)子类对象可以被视为其父类的一个对象(因为子类继承了父类所有的属性和方法,所以子类对象可以访问父类的成员)
如:一个Student对象也是一个Person对象。
(2)父类对象不能被当做其某一个子类的对象(因为“父类的成员<=子类的成员”,所以对于子类对象所具有某些属性和方法,父类对象可能并不具有。)
注:如果父类的引用变量/父类的对象引用指向的实际是子类对象,则可把这个引用变量(即我们所俗称的父类的对象)当作子类对象使用。
(3)如果一个方法的形式参数定义的是父类对象,那么调用这个方法时,可以把子类对象作为实际参数(因为“子类对象可以被视为其父类的一个对象”)
2.8访问修饰符
1.类的访问修饰符:public、默认
(1)public类可以在任何地方被访问
(2)默认类只可以被同一个包内部的类访问
2.类的成员的四种访问修饰符
2.9 super关键字
1.类的成员:指类的属性、方法
2.子类访问父类成员时用super关键字,代表父类对象(与this代表当前对象有相似用法)
3.三个作用:
(1)在子类构造方法的第一句调用父类的构造方法。要求:
①必须在子类构造方法的第一句去调用
②无论是父类的有参构造方法,还是父类的无参构造方法,只要父类显式定义了,就能通过这种方法调用。
例:父类Person有一个构造方法Person(String name, int age),不能说子类Student也自动有一个构造方法Student(String name, int age)。但子类在构造方法的第一句,可以用super来调用父类的构造方法。
- Student(String name, int age, String school ){
- super( name, age );
- this.school= school;
- }
(2)用“super.属性名/方法名”读写父类属性、调用父类方法(相当于用父类的对象调用):由于继承,所以使用this也可访问父类的属性变量和方法。但有时为了明确地指明是父类的属性和方法,就要用关键字super(典型的比如:被子类所隐藏了的父类的同名变量、同名方法,通过super就实现了在覆盖父类的属性方法的同时,又利用父类已定义好的属性和方法)。
例如:父类Student有一个属性变量age,在子类Student中用age, this.age, super.age读写age是完全一样的:
- void testThisSuper(){
- int a;
- a = age;
- a = this.age;
- a = super.age;
- }
4.实例
(1)父类Pet
- public class Pet {
- //父类:定义属性和方法
- private String nick;
- int age;
- protected String color;
- public String type="国产宠物";
- //父类的有参构造方法
- public Pet(String nick, int age, String color) {
- this.nick = nick;
- this.age = age;
- this.color = color;
- }
- //父类可继承给子类的普通方法
- public void run() {
- System.out.println("宠物在跑!");
- }
- }
(2)子类
- public class Dog extends Pet{
- //定义子类的特有属性(父类的可继承属性直接继承,来自父类的同名属性会被子类覆盖)
- String strain;
- String type="国产狗狗";
- //子类的属性可能既有父类定义的、又有子类定义的,下示方法实现了在一个构造方法内全部初始化
- public Dog(String nick, int age, String color,String strain) {
- //Dog类的对象拿到前三个参数后自己不处理,直接使用super关键字,手动调用父类构造方法交给父类
- super(nick, age, color);//在子类的构造方法调用父类的构造方法,需写在子类构造方法的第一行
- this.strain=strain;//Dog的对象拿到第四个参数后赋给Dog类当前Dog对象的strain属性
- }
- public void show() {
- //在子类用super关键字访问父类的属性
- System.out.println(super.type);
- //在子类用super关键字调用父类的可继承普通方法
- super.run();
- }
- }
(3)test
- public class Test {
- public static void main(String[] args) {
- //在class Test内创建Dog对象,并对象实例化
- //前三个参数:测试用super关键字在子类构造方法内调用父类构造方法
- //第四个参数:测试在子类属性既有父类的定义又有子类定义的时,在一个构造方法内实现全部的初始化
- Dog d=new Dog("小黑",2,"黑色","哈士奇");
- //调用Dog类的public方法,先测试在子类用super关键字访问父类的属性
- //调用Dog类的public方法,再测试在子类用super关键字调用父类的可继承普通方法
- d.show();
- }
- }
2.10 final关键字
1.final放在访问修饰符前,可用来修饰类、方法、变量。
(1)final修饰的类:该类不能被继承;
(2)final修饰的方法:该方法不能被子类重写
(3)final修饰的变量:该变量的值不能被修改(即只读量、能且只能被赋值一次)
2.final的使用
(1)如果想让一个类永远不被继承,就可用final修饰。final类中的所有方法都会被隐式地指定为final方法,但不影响对变量的定义。final类创建的对象、属性、方法是只可读的,在多线程可以安全共享
(2)final变量
①final修饰普通变量:如果是基本数据类型的变量,则其数值一旦在初始化后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
②final修饰属性变量:必须在声明时初始化或在构造器中初始化,否则会报编译错误
③final修饰局部变量:该局部变量必须且只能赋值一次。它的值可能不是常量,但它的取值在变量存在期间不会改变。
(3)如果想明确禁止父类某方法在子类中被重写覆盖,则将该方法设为final的
3.一个变量被static final两个修饰符所限定时,表示常量:
如:Integer. MAX_VALUE(表示最大整数)、Math.PI(表示圆周率)就是这种常量。
注:若不给“static final变量”赋初值,则JVM按数据类型赋予默认值(数值为0,boolean型为false,引用型为null)
4.补充:
(1)类的private方法会隐式地被指定为final方法
(2)接口中声明的所有变量本身是final的
(3)final写在访问修饰符前,abstract写在访问修饰符后。final和abstract这两个关键字是反相关的,是final的类就不可能是abstract的
2.11 抽象类与抽象方法[抽象方法只能定义于抽象类内]
1.抽象类是在继承关系中对父类的一种设置,当需限制父类的实例化时(即不允许创建父类的对象时),则可使用abstract修饰父类。
①创建父类的对象没有意义,因为继承关系中父类的作用是定义子类们的公有属性和公有方法以避免定义的重复,而且在父类创建的那些属性本身也不需要值,因父类所定义的属性只是对子类的一个规范。
②所以一般本身就不需要给父类作实例化、不给父类的属性赋值,只创建子类的对象。
注1:abstract修饰父类后,不影响其继承,只限制其实例化
注2:抽象类中可以包含abstract方法,也可以不包含abstract方法。但一旦某个类中包含了abstract方法,这个类就必须得用abstract去修饰。
2.被abstract修饰的方法叫抽象方法,抽象方法的作用是为所有子类对象定义一个统一的接口。对抽象方法只需声明、不需实现,即用分号“;”而不是用“{ }”。格式如下:
访问修饰符 abstract 返回值类型 方法名(参数列表);
例:public abstract void eat();
注:对于父类的抽象方法,子类必须重写去实现,除非子类也是抽象类
3.使用抽象方法的目的:
①使父类更加简洁纯粹,只做规范而不用涉及具体内容。
②对子类的方法进行了规范(方法名、参数、返回值都必须与父类一致),要求了非抽象子类必须重写实现这些方法。
③抽象类专为继承而生(抽象类不能进行实例化,抽象方法不能有方法体,abstract修饰就是服务于继承的)
2.12Object类
1.类和类之间有继承关系,然而当我们以现实世界的对象做参考时会发现几乎所有的类都有父类。编程中也是如此,Object类就是所有类的最终父类。对于任何一个不写明父类的类(该类本身可能就是某些类的父类),该类就会把其父类默认为是Object类,并自动继承。
【理解:Object是“东西”的意思,“东西”这两个字可以代指各种类的所有的对象】
2.Object类作为一个类也是能实例化的。例:Object obj1=new Object();
3. Object类所定义的“对象名.equals()方法”:
(1)对于两个字符串,“==”判断的是两个字符串的内存地址是否相等,“.equals()”判断的是两个字符串的值是否相等。Object类所定义的.equals()方法和==意义相同,即判断内存地址是否相等。
既然Object类是所有类的最终父类,那为什么当操作对象是字符串时,Object类所定义的.equals()方法就变成了判断两个字符串的值是否相等了呢?因为字符串也是一个类、也有.equals()方法,但字符串类对.equals()方法进行了方法重写,使其成为了判断字符串值相等的方法。
(2)引用变量可以作为参数传入方法,.equals()方法的参数就可以是指向某个对象的引用变量。对于两个引用变量的比较,判断内存地址是否相同没有意义。因为两个引用变量的地址是肯定不同的。一般我们判断的是两个引用变量的引用是否相同,即看这两个引用变量是否指向同一个对象。这时候用“==”判断
注:引用值内包含了跟地址相关的信息,但是是由java虚拟机管理或是公共语言运行库管理的,所以比直接用内存地址更安全。
4.以一个普通的Dog类为例
- public class Dog{
- //一个很普通的类
- //定义这个很普通的类的属性
- String nick;
- int age;
- String color;
- //定义这个很普通的类的构造方法·
- public Dog(String nick, int age, String color) {
- this.nick = nick;
- this.age = age;
- this.color = color;
- }
- //Object类中的.equals()方法和==意义相同:判断内存地址是否相等
- //重写Object类中定义的.equals()方法,使其成为判断字符串值相等的方法
- //因为返回值是true、false,故返回值类型是boolean
- //对象可以作为参数传入方法,这里的参数就是对象
- public boolean equals(Dog d) {
- //只有被传入对象的各个属性的值都相等时,才返回true,否则返回false
- if(this.nick.equals(d.nick) && this.color.equals(d.color) && this.age==d.age) {
- return true;
- }else {
- return false;
- }
- }
- }
5.Test.java
- public class Test {
- public static void main(String[] args) {
- // Object类也可以像正常类一样的去创建对象
- Object obj=new Object();
- //新建Dog类的两个属性值完全相同的对象
- Dog d1=new Dog("小黑",2,"黑色");
- Dog d2=new Dog("小黑",2,"黑色");
- //==判断的是内存地址是否相等,验证d1、d2两个值相同的对象的内存地址不等
- //Object类中的.equals()方法和==意义相同:判断内存地址是否相等
- System.out.println(d1==d2);
- //对两个字符串用.equals()方法时就是判断字符串的值是否相等
- //因为字符串也是一个类、也会从默认父类Object类继承.equals()方法,但字符串类对.equals()方法进行了方法重写,使其成为判断字符串值相等的方法
- //Dog类没有指明父类,则默认父类是Object类,自然会默认继承Object类所定义的.equals()方法
- //在Dog类内重写Object类的.equals()方法后,验证方法重写的有效性
- System.out.println(d1.equals(d2));
- }
- }
2.13实例—涨工资
1.描述.
2.pojo实体包里的代码:
3.test检测包里的代码:
2.14接口类(interface)
1.一个父类可有多个子类,但某子类只能直接继承一个父类。即类只能继承于一个类。为了解决Java类不能多继承的问题,提出了接口的概念。即一个类只能直接继承(用关键字extends)一个父类,但可实现(用关键字implements)多个接口。接口虽然只能继承接口,但一个接口却可继承(用关键字extends)多个父接口(逗号隔开)
注:“接口之间的继承”和“类之间的继承”,都是用extends关键字的继承关系
注:子接口继承父接口中所有的常量和方法,但若在子接口中定义了和父接口同名的常量或方法,则父接口中的常量被隐藏、方法被覆盖。
2.定义:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
解释:接口可以理解为一种特殊的类,是由全局静态常量和公共抽象方法组成。接口是解决Java无法使用多继承的一种手段,但在实际中接口存在的更多的作用是用于制定标准/规范。且接口中的方法本身也必须全都是抽象方法。
3.两种关系:
(1)继承描述的是:is-a关系,即:子类is a 父类。
(2)接口描述的是:has-a关系,即:某类has a 其接口类的功能。
例1:智能手机与电话与MP3、MP4
因为,智能手机是电话的一种,只不过拥有了MP3、MP4的功能。
所以,父类:电话类;子类:智能手机类;接口类:MP3类、MP4类
例2:狗、猫与动物与宠物
①因为,狗、猫是动物的一种,只不过可以作为宠物来饲养。
所以,父类:动物类;子类:狗类、猫类;接口类:宠物类
②因为,狗、猫既可以作为动物来饲养,又可以作为宠物来饲养。
所以,接口类:动物类、宠物类;普通类:狗类、猫类
例3:高科技防盗门与门与锁与相机
因为,高科技防盗门是门的一种,只不过拥有了上锁、相机的功能
所以,父类:门;子类:高科技防盗门;接口:锁类、相机类
4.“实现”二字是何含义:
①接口间的继承称实现,即若接口1继承借口2,则称接口1实现接口2。
②非接口类继承接口类,称这个非接口类为实现类,称这个接口类被实现。
③把抽象方法的方法体编写完整,称作抽象方法的实现(是一个方法重写的过程,接口的方法自动是public abstract的方法)
5.定义接口时,需用关键字interface
- [public] interface interface_name [extends interface1_name,…, interfaceN_name] {
- // 接口体,其中可以包含常量的定义和抽象方法的声明
- [public] [static] [final] type 常量名 = value; // 定义常量,可为多个
- [public] [abstract] returnType 方法名 (parameter_list); //声明抽象方法,可为多个
- }
(1)访问修饰符:public表明任意类均可实现这个接口;默认表明只有与该接口在同一个包中的类才可以访问这个接口。
(2)接口名以able或ible结尾,表明接口能完成一定的行为。如flyable
(3)在接口中定义的常量默认具有public static final修饰,定义的方法默认具有public abstract修饰:接口体中的变量都是全局(一定定义于接口内方法外)静态(static的变量可用类名直接调用)常量(因默认static final修饰,所以值不可更改);接口体中的方法都只是声明但未实现的方法(即接口中的方法都没有方法体)
(4)接口不可实例化【抽象类也不可实例化】,接口没有构造方法
6.接口的实现:
(1)在类的声明中用implements子句来表示一个类使用某个接口,一个类可以实现多个接口
(2)在类体中可使用在接口中定义的常量
(3)实现类必须实现该接口的所有方法(因接口中定义的所有方法都是没有方法体的抽象方法,故实现类必须对这些抽象方法进行方法重写,才能让这些方法有实际意义),否则报错
7.接口数据类型
(1)接口可以作为一种引用类型来使用。任何实现该接口的类的实例的引用都可以存储在该接口类型的引用变量中,通过这些变量可以访问类所实现的接口中的方法。
(2)把接口作为一种数据类型使用,可以不需了解接口数据类型变量所储存的引用所指向的对象的所属的类是哪个,因为JVM动态地确定该使用哪个类中的方法。即多态。
8.方法重写与方法重载
(1)Override:方法重写,是覆盖了一个方法并且对其重写,以求达到不同的作用。方法重写是Java多态机制的核心。最熟悉的方法重写就是对接口/父类方法的实现
①在接口中一般只是对方法进行了声明,而在实现类中就需要通过方法重写去实现接口所声明的所有方法
②主要用于子类和父类之间:在父类中定义了一个方法,同时在子类中对这个方法进行重写,以实现子类行为的特殊化
(2)Overload:译为重载,指方法名相同而参数列表不同的方法。
方法重载描述的一定是同一个类中的方法的关系,方法重载提高了对于某作用的方法对参数数量、参数类型的包容度。在调用重载方法时:系统能自动根据参数的类型和数量自动地选择对应的正确的同名方法去调用。
2.15接口的使用
1.定义接口时:接口体的属性是public static final属性(即全局静态常量),接口体的方法是public abstract方法;接口类没有构造方法;接口类无法实例化
2.因为接口中定义的都是抽象方法,所以若实现一个接口,就必须实现接口中定义的所有方法(即重写接口所定义的所有方法)
3.对于一个普通类,若它既有父类来继承又有多个接口来实现:
①public class 类名 extends 父类类名 implements 接口1名,… ,接口N名 { 类体 }
②接口中所定义的方法必须全部重写,父类中所定义的方法是否需重写根据实际要求
4.接口是一种能力,体现在接口的方法上;在实际中接口存在的更多的作用是制定标准/规范
5.面向接口编程:关心实现类有何能力,而不关心实现细节
比如智能手机类,智能手机类能有哪些能力取决于该类实现了哪些接口,智能手机类功能很多,自然需要实现很多的接口;那么在定义这些接口时,并不需要考虑如何才能实现这些接口所定义的方法,只需定义好即可;至于这些方法如何才能实现,对于定义接口时而言,并不需要关注
6.实例---- 电话类是智能手机类的父类,MP3类、Map类、Wallet类是智能手机类的接口。
(1)电话类(智能手机is a电话→电话类是智能手机类的父类)
- //电话类是父类,在父类写一个有普遍意义的公共方法、写一个仅起规范作用的抽象方法
- public abstract class Phone {
- public abstract void call();
- }
(2)MP3类(智能手机has a MP3播放音乐的功能→MP3类是智能手机类的接口)
- //接口的属性必须是全局静态常量、方法必须是public抽象方法
- //接口没有构造方法,接口无法实例化
- public interface Mp3 {
- String discrib="这是MP3接口!";
- //在MP3接口内定义抽象方法playMusic()
- public abstract void playMusic();
- }
(3)Map类(智能手机has a Map查看地图的功能→Map类是智能手机类的接口)
- //在接口Map内定义抽象方法shouMap
- public interface Map {
- public abstract void showMap();
- }
(4)Wallet类(智能手机has a Wallet付款的功能→Wallet类是智能手机类的接口)
- //在接口Wallet内定义抽象方法pay
- public interface Wallet {
- public abstract void pay();
- }
(5)智能手机类
- //电话类是智能手机类的父类,MP3类、Map类、Wallet类是智能手机类的接口
- public class CellPhone extends Phone implements Mp3,Map,Wallet {
- //因为接口中定义的都是抽象方法,所以每实现一个接口,就必须实现该接口中定义的所有方法
- //子类可以根据需要,借助对父类方法的重写去定义特定于自己的行为
- //在子类重写父类所定义的抽象方法call
- public void call() {
- System.out.println("开始打电话!");
- }
- //在实现类重写接口所定义的抽象方法
- public void playMusic() {
- System.out.println("开始播放音乐");
- }
- public void pay() {
- System.out.println("打开支付宝开始付款!");
- }
- public void showMap() {
- System.out.println("打开支地图,开始导航!");
- }
- }
(6)用class Test来检测所写父类、子类、接口的有效性
- public class Test {
- //Tset类需要执行,故需要main方法
- public static void main(String[] args) {
- CellPhone cp=new CellPhone();
- cp.call(); //测试子类重写的call方法是否有效
- cp.playMusic(); //测试实现类重写的playMusic方法是否有效
- cp.pay(); //测试实现类重写的pay方法是否有效
- cp.showMap(); //测试实现类重写的showMap方法是否有效
- }
- }
注:本文由H同志编写,欢迎批评指正、交流探讨
JAVA基础第二章 面向对象进阶相关推荐
- JAVA基础——第二章,变量,数据类型和运算符
一,变量声明及使用 申明变量再赋值 int money; //声明变量 monry = 100; //赋值 声明变量并赋值 int money = 100; //声明变量并赋值 二,JAVA常用数据类 ...
- Java算法--第二章--查找与排序(2)递归基础--佩波那契最大公约数插入排序汉诺塔
Java算法–第二章–查找与排序(2)递归基础 一.找重复 1.找到一种划分方法 2.找到递推公式或者等价转换 都是父问题转化为求解子问题 二.找变化的量 变化的量通常要作为参数 三.找出出口 代码: ...
- Java基础教程:面向对象编程[2]
Java基础教程:面向对象编程[2] 内容大纲 访问修饰符 四种访问修饰符 Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java 支持 4 种不同的访问权限. default ...
- java 3D 第二章 java 3D基本概念
java 3D 第二章 java 3D基本概念 java 3D基本概念 java 3D的包及其功能 java 3D 高分辨率大尺度坐标 Java 3D场景图(Scene Graph) VirtualU ...
- ZeroMQ 中文指南 第二章 ZeroMQ进阶【转载】
此文章转载自GitHub : https://github.com/anjuke/zguide-cn 作者信息如下. ZMQ 指南 作者: Pieter Hintjens ph@imatix.com, ...
- 计算机技术的应用 课件,计算机技术及应用基础――第二章ppt课件
<计算机技术及应用基础――第二章ppt课件>由会员分享,可在线阅读,更多相关<计算机技术及应用基础――第二章ppt课件(64页珍藏版)>请在人人文库网上搜索. 1.第二章 VB ...
- 计算机文化基础第二章,计算机文化基础(第二章Windows2000操作系统)
计算机文化基础(第二章Windows2000操作系统) 第二章Windows 2000 操作系统1. 打开"资源管理器"的方法不能是_A右击"开始"按钮 B选择 ...
- 第二章 面向对象的编程风格
第二章 面向对象的编程风格 2.1 如何撰写函数 函数定义四要素:函数的返回类型.函数的名称.函数的参数列表.函数体 占位符(placeholder) 函数原型(function prototype) ...
- PHP核心技术与最佳实践 读书笔记 第二章 面向对象的设计原则
2019独角兽企业重金招聘Python工程师标准>>> 第二章 面向对象的设计原则 2.1 面向对象设计的五大原则 单一职责原则 接口隔离原则 开放-封闭原则 替换原则 依赖倒置原则 ...
最新文章
- vue使用pwa_如何使用HTML,CSS和JavaScript从头开始构建PWA
- java 无限级_JAVA+Hibernate 无限级分类
- IOS7为什么遭吐槽?
- Char RNN原理介绍以及文本生成实践
- MATLAB中的光照处理
- 微服务架构的服务与发现-Spring Cloud
- 计算机无故重启是什么原因,事实:无缘无故重新启动计算机有什么问题?计算机无故重启的原因和解决方法...
- JQuery中使用cookie记住背景颜色
- 条形码、二维码、三维码解读
- pscc2019滤镜抽出_Photoshop(ps)cc2019 已经发现你啦!
- 苹果新系统很鸿蒙!iPad终于能写代码了,iPhone竟成异地恋神器 | WWDC 2021
- 内网渗透之 windows 基础
- oracle存货转资产,存货转固定资产账务处理
- GitHub简介、fork、pull和clone、快速起步
- pandas 中上下两行相减(隔行相减) -- shift函数的使用
- 使用drawio画地图
- 基于Cortex-A53内核Linux系统gec6818开发板的电子自助点餐设计
- 电机控制中标幺的目的
- 人工智能专家细数AI安全隐患
- 抓包神器:Fiddler Everywhere