精华内容
下载资源
问答
  • java反射详解【转】与 反射是否会破坏类的封装性见解
    千次阅读
    2018-03-11 17:58:19

    问题:反射是否会破坏类的封装性见解

           首先,封装,是将具体的实现细节隐藏,而把功能作为整体提供给类的外部使用,也就是说,公有方法能够完成类所具有的功能。当别人使用这个类时,如果通过反射直接调用私有方法,可能根本实现不了类的功能,甚至可能会出错,因此通过反射调用私有方法可以说是没有任何用处的,开发人员没有必要故意去破坏封装好的类。从这点上看,封装性并没有被破坏。(摘自百度问答)

        个人的见解:反射确实可以得到一切 类中的东西(包括私有的属性、方法等),但是或许不算是破坏封装,私有方法是为了让继承的类无法使用,避免调用那些被设为私有的方法出现一些不必要的错误。这就是封装性。反射虽然可以获取私有方法并使用方法,只能说是其功能强大,可以在保证在调用私有方法不会出现错误,但是并没有反射调用方法后,该方法就不是私有的了。他仍然是私有的,仍然在子类中不可见。

        对于是否破坏了封装性,也欢迎大家评论区说明自己的观点。

    =================================================================================

    //以下内容转发自blog:http://blog.csdn.net/sinat_38259539/article/details/71799078

    反射是框架设计的灵魂

    (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

    一、反射的概述

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

    以上的总结就是什么是反射

    反射就是把java类中的各种成分映射成一个个的Java对象

    例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。

         (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)

    如图是类的正常加载过程:反射的原理在与class对象。

    熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

     

    其中这个Class对象很特殊。我们先了解一下这个Class类

    二、查看Class类在java中的api详解(1.7的API)

    如何阅读java中的api详见java基础之——String字符串处理

     

    Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)

    Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

    没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧

     

    三、反射的使用(这里使用Student类做演示)

    先写一个Student类。

    1、获取Class对象的三种方式

    1.1 Object ——> getClass();
    1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
    1.3 通过Class类的静态方法:forName(String  className)(常用)

    其中1.1是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取

     

    /** 
     * 获取Class对象的三种方式 
     * 1 Object ——> getClass(); 
     * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性 
     * 3 通过Class类的静态方法:forName(String  className)(常用) 
     * 
     */  
    public class Fanshe {  
        public static void main(String[] args) {  
            //第一种方式获取Class对象    
            Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。  
            Class stuClass = stu1.getClass();//获取Class对象  
            System.out.println(stuClass.getName());  
              
            //第二种方式获取Class对象  
            Class stuClass2 = Student.class;  
            System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个  
              
            //第三种方式获取Class对象  
            try {  
                Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名  
                System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象  
            } catch (ClassNotFoundException e) {  
                e.printStackTrace();  
            }  
              
        }  
    }

    注意:在运行期间,一个类,只有一个Class对象产生。

    三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。

     

    2、通过反射获取构造方法并使用:

    student类:

    
    public class Student {  
          
        //---------------构造方法-------------------  
        //(默认的构造方法)  
        Student(String str){  
            System.out.println("(默认)的构造方法 s = " + str);  
        }  
          
        //无参构造方法  
        public Student(){  
            System.out.println("调用了公有、无参构造方法执行了。。。");  
        }  
          
        //有一个参数的构造方法  
        public Student(char name){  
            System.out.println("姓名:" + name);  
        }  
          
        //有多个参数的构造方法  
        public Student(String name ,int age){  
            System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。  
        }  
          
        //受保护的构造方法  
        protected Student(boolean n){  
            System.out.println("受保护的构造方法 n = " + n);  
        }  
          
        //私有构造方法  
        private Student(int age){  
            System.out.println("私有的构造方法   年龄:"+ age);  
        }  
      
    }  
    

    测试类:

    import java.lang.reflect.Constructor;  
      
      
    /* 
     * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员; 
     *  
     * 1.获取构造方法: 
     *      1).批量的方法: 
     *          public Constructor[] getConstructors():所有"公有的"构造方法 
                public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有) 
          
     *      2).获取单个的方法,并调用: 
     *          public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法: 
     *          public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有; 
     *       
     *          调用构造方法: 
     *          Constructor-->newInstance(Object... initargs) 
     */  
    public class Constructors {  
      
        public static void main(String[] args) throws Exception {  
            //1.加载Class对象  
            Class clazz = Class.forName("fanshe.Student");  
              
              
            //2.获取所有公有构造方法  
            System.out.println("**********************所有公有构造方法*********************************");  
            Constructor[] conArray = clazz.getConstructors();  
            for(Constructor c : conArray){  
                System.out.println(c);  
            }  
              
              
            System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");  
            conArray = clazz.getDeclaredConstructors();  
            for(Constructor c : conArray){  
                System.out.println(c);  
            }  
              
            System.out.println("*****************获取公有、无参的构造方法*******************************");  
            Constructor con = clazz.getConstructor(null);  
            //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型  
            //2>、返回的是描述这个无参构造函数的类对象。  
          
            System.out.println("con = " + con);  
            //调用构造方法  
            Object obj = con.newInstance();  
        //  System.out.println("obj = " + obj);  
        //  Student stu = (Student)obj;  
              
            System.out.println("******************获取私有构造方法,并调用*******************************");  
            con = clazz.getDeclaredConstructor(char.class);  
            System.out.println(con);  
            //调用构造方法  
            con.setAccessible(true);//暴力访问(忽略掉访问修饰符)  
            obj = con.newInstance('男');  
        }  
          
    }  

    后台输出:

    **********************所有公有构造方法*********************************  
    public fanshe.Student(java.lang.String,int)  
    public fanshe.Student(char)  
    public fanshe.Student()  
    ************所有的构造方法(包括:私有、受保护、默认、公有)***************  
    private fanshe.Student(int)  
    protected fanshe.Student(boolean)  
    public fanshe.Student(java.lang.String,int)  
    public fanshe.Student(char)  
    public fanshe.Student()  
    fanshe.Student(java.lang.String)  
    *****************获取公有、无参的构造方法*******************************  
    con = public fanshe.Student()  
    调用了公有、无参构造方法执行了。。。  
    ******************获取私有构造方法,并调用*******************************  
    public fanshe.Student(char)  
    姓名:男  
    

    调用方法:

    1.获取构造方法:

      1).批量的方法:

    • public Constructor[] getConstructors():所有"公有的"构造方法
    • public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

    2).获取单个的方法,并调用:

    • public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
    • public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

      调用构造方法:

    • Constructor-->newInstance(Object... initargs)

    2、newInstance是 Constructor类的方法(管理构造函数的类)

    api的解释为:

    • newInstance(Object... initargs)
    •            使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
    • 它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

    3、获取成员变量并调用

    student类:

    public class Student {  
        public Student(){  
              
        }  
        //**********字段*************//  
        public String name;  
        protected int age;  
        char sex;  
        private String phoneNum;  
          
        @Override  
        public String toString() {  
            return "Student [name=" + name + ", age=" + age + ", sex=" + sex  
                    + ", phoneNum=" + phoneNum + "]";  
        }  
          
          
    }

    测试类:

    import java.lang.reflect.Field;  
    /* 
     * 获取成员变量并调用: 
     *  
     * 1.批量的 
     *      1).Field[] getFields():获取所有的"公有字段" 
     *      2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有; 
     * 2.获取单个的: 
     *      1).public Field getField(String fieldName):获取某个"公有的"字段; 
     *      2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的) 
     *  
     *   设置字段的值: 
     *      Field --> public void set(Object obj,Object value): 
     *                  参数说明: 
     *                  1.obj:要设置的字段所在的对象; 
     *                  2.value:要为字段设置的值; 
     *  
     */  
    public class Fields {  
      
            public static void main(String[] args) throws Exception {  
                //1.获取Class对象  
                Class stuClass = Class.forName("fanshe.field.Student");  
                //2.获取字段  
                System.out.println("************获取所有公有的字段********************");  
                Field[] fieldArray = stuClass.getFields();  
                for(Field f : fieldArray){  
                    System.out.println(f);  
                }  
                System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");  
                fieldArray = stuClass.getDeclaredFields();  
                for(Field f : fieldArray){  
                    System.out.println(f);  
                }  
                System.out.println("*************获取公有字段**并调用***********************************");  
                Field f = stuClass.getField("name");  
                System.out.println(f);  
                //获取一个对象  
                Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();  
                //为字段设置值  
                f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"  
                //验证  
                Student stu = (Student)obj;  
                System.out.println("验证姓名:" + stu.name);  
                  
                  
                System.out.println("**************获取私有字段****并调用********************************");  
                f = stuClass.getDeclaredField("phoneNum");  
                System.out.println(f);  
                f.setAccessible(true);//暴力反射,解除私有限定  
                f.set(obj, "18888889999");  
                System.out.println("验证电话:" + stu);  
                  
            }  
        }


    后台输出:

    ************获取所有公有的字段********************  
    public java.lang.String fanshe.field.Student.name  
    ************获取所有的字段(包括私有、受保护、默认的)********************  
    public java.lang.String fanshe.field.Student.name  
    protected int fanshe.field.Student.age  
    char fanshe.field.Student.sex  
    private java.lang.String fanshe.field.Student.phoneNum  
    *************获取公有字段**并调用***********************************  
    public java.lang.String fanshe.field.Student.name  
    验证姓名:刘德华  
    **************获取私有字段****并调用********************************  
    private java.lang.String fanshe.field.Student.phoneNum  
    验证电话:Student [name=刘德华, age=0, sex=  


    由此可见

    调用字段时:需要传递两个参数:

    Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
    //为字段设置值
    f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"

    第一个参数:要传入设置的对象,第二个参数:要传入实参

    4、获取成员方法并调用

    student类:

    public class Student {  
        //**************成员方法***************//  
        public void show1(String s){  
            System.out.println("调用了:公有的,String参数的show1(): s = " + s);  
        }  
        protected void show2(){  
            System.out.println("调用了:受保护的,无参的show2()");  
        }  
        void show3(){  
            System.out.println("调用了:默认的,无参的show3()");  
        }  
        private String show4(int age){  
            System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);  
            return "abcd";  
        }  
    }  

    测试类:

    import java.lang.reflect.Method;  
      
    /* 
     * 获取成员方法并调用: 
     *  
     * 1.批量的: 
     *      public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类) 
     *      public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的) 
     * 2.获取单个的: 
     *      public Method getMethod(String name,Class<?>... parameterTypes): 
     *                  参数: 
     *                      name : 方法名; 
     *                      Class ... : 形参的Class类型对象 
     *      public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 
     *  
     *   调用方法: 
     *      Method --> public Object invoke(Object obj,Object... args): 
     *                  参数说明: 
     *                  obj : 要调用方法的对象; 
     *                  args:调用方式时所传递的实参; 
     
    ): 
     */  
    public class MethodClass {  
      
        public static void main(String[] args) throws Exception {  
            //1.获取Class对象  
            Class stuClass = Class.forName("fanshe.method.Student");  
            //2.获取所有公有方法  
            System.out.println("***************获取所有的”公有“方法*******************");  
            stuClass.getMethods();  
            Method[] methodArray = stuClass.getMethods();  
            for(Method m : methodArray){  
                System.out.println(m);  
            }  
            System.out.println("***************获取所有的方法,包括私有的*******************");  
            methodArray = stuClass.getDeclaredMethods();  
            for(Method m : methodArray){  
                System.out.println(m);  
            }  
            System.out.println("***************获取公有的show1()方法*******************");  
            Method m = stuClass.getMethod("show1", String.class);  
            System.out.println(m);  
            //实例化一个Student对象  
            Object obj = stuClass.getConstructor().newInstance();  
            m.invoke(obj, "刘德华");  
              
            System.out.println("***************获取私有的show4()方法******************");  
            m = stuClass.getDeclaredMethod("show4", int.class);  
            System.out.println(m);  
            m.setAccessible(true);//解除私有限定  
            Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参  
            System.out.println("返回值:" + result);  
              
              
        }  
    }  

    控制台输出:

    ***************获取所有的”公有“方法*******************  
    public void fanshe.method.Student.show1(java.lang.String)  
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException  
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException  
    public final void java.lang.Object.wait() throws java.lang.InterruptedException  
    public boolean java.lang.Object.equals(java.lang.Object)  
    public java.lang.String java.lang.Object.toString()  
    public native int java.lang.Object.hashCode()  
    public final native java.lang.Class java.lang.Object.getClass()  
    public final native void java.lang.Object.notify()  
    public final native void java.lang.Object.notifyAll()  
    ***************获取所有的方法,包括私有的*******************  
    public void fanshe.method.Student.show1(java.lang.String)  
    private java.lang.String fanshe.method.Student.show4(int)  
    protected void fanshe.method.Student.show2()  
    void fanshe.method.Student.show3()  
    ***************获取公有的show1()方法*******************  
    public void fanshe.method.Student.show1(java.lang.String)  
    调用了:公有的,String参数的show1(): s = 刘德华  
    ***************获取私有的show4()方法******************  
    private java.lang.String fanshe.method.Student.show4(int)  
    调用了,私有的,并且有返回值的,int参数的show4(): age = 20  
    返回值:abcd  

    由此可见:

    m = stuClass.getDeclaredMethod("show4", int.class);//调用制定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
    System.out.println(m);
    m.setAccessible(true);//解除私有限定
    Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
    System.out.println("返回值:" + result);//

    控制台输出:

    ***************获取所有的”公有“方法*******************  
    public void fanshe.method.Student.show1(java.lang.String)  
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException  
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException  
    public final void java.lang.Object.wait() throws java.lang.InterruptedException  
    public boolean java.lang.Object.equals(java.lang.Object)  
    public java.lang.String java.lang.Object.toString()  
    public native int java.lang.Object.hashCode()  
    public final native java.lang.Class java.lang.Object.getClass()  
    public final native void java.lang.Object.notify()  
    public final native void java.lang.Object.notifyAll()  
    ***************获取所有的方法,包括私有的*******************  
    public void fanshe.method.Student.show1(java.lang.String)  
    private java.lang.String fanshe.method.Student.show4(int)  
    protected void fanshe.method.Student.show2()  
    void fanshe.method.Student.show3()  
    ***************获取公有的show1()方法*******************  
    public void fanshe.method.Student.show1(java.lang.String)  
    调用了:公有的,String参数的show1(): s = 刘德华  
    ***************获取私有的show4()方法******************  
    private java.lang.String fanshe.method.Student.show4(int)  
    调用了,私有的,并且有返回值的,int参数的show4(): age = 20  
    返回值:abcd  
    


    其实这里的成员方法:在模型中有属性一词,就是那些setter()方法和getter()方法。还有字段组成,这些内容在内省中详解

    5、反射main方法

    student类:

    public class Student {  
      
        public static void main(String[] args) {  
            System.out.println("main方法执行了。。。");  
        }  
    }  

    测试类:

    import java.lang.reflect.Method;  
      
    /** 
     * 获取Student类的main方法、不要与当前的main方法搞混了 
     */  
    public class Main {  
          
        public static void main(String[] args) {  
            try {  
                //1、获取Student对象的字节码  
                Class clazz = Class.forName("fanshe.main.Student");  
                  
                //2、获取main方法  
                 Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,  
                //3、调用main方法  
                // methodMain.invoke(null, new String[]{"a","b","c"});  
                 //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数  
                 //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。  
                 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一  
                // methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二  
                  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
              
              
        }  
    }

    控制台输出:

    main方法执行了。。。

    6、反射方法的其它使用之---通过反射运行配置文件内容

    student类:

    public class Student {  
        public void show(){  
            System.out.println("is show()");  
        }  
    }  
    

    配置文件以txt文件为例子(pro.txt):

    className = cn.fanshe.Student  
    methodName = show  

    测试类:

    import java.io.FileNotFoundException;  
    import java.io.FileReader;  
    import java.io.IOException;  
    import java.lang.reflect.Method;  
    import java.util.Properties;  
      
    /* 
     * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改 
     * 我们只需要将新类发送给客户端,并修改配置文件即可 
     */  
    public class Demo {  
        public static void main(String[] args) throws Exception {  
            //通过反射获取Class对象  
            Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"  
            //2获取show()方法  
            Method m = stuClass.getMethod(getValue("methodName"));//show  
            //3.调用show()方法  
            m.invoke(stuClass.getConstructor().newInstance());  
              
        }  
          
        //此方法接收一个key,在配置文件中获取相应的value  
        public static String getValue(String key) throws IOException{  
            Properties pro = new Properties();//获取配置文件的对象  
            FileReader in = new FileReader("pro.txt");//获取输入流  
            pro.load(in);//将流加载到配置文件对象中  
            in.close();  
            return pro.getProperty(key);//返回根据key获取的value值  
        }  
    }  
    

    控制台输出:

    is show()

    需求:
    当我们升级这个系统时,不要Student类,而需要新写一个Student2的类时,这时只需要更改pro.txt的文件内容就可以了。代码就一点不用改动

    要替换的student2类:

    public class Student2 {  
        public void show2(){  
            System.out.println("is show2()");  
        }  
    }  

    配置文件更改为:

    className = cn.fanshe.Student2  
    methodName = show2  

    控制台输出:

    is show2();

    7、反射方法的其它使用之---通过反射越过泛型检查

    泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的

    测试类:

    import java.lang.reflect.Method;  
    import java.util.ArrayList;  
      
    /* 
     * 通过反射越过泛型检查 
     *  
     * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值? 
     */  
    public class Demo {  
        public static void main(String[] args) throws Exception{  
            ArrayList<String> strList = new ArrayList<>();  
            strList.add("aaa");  
            strList.add("bbb");  
              
        //  strList.add(100);  
            //获取ArrayList的Class对象,反向的调用add()方法,添加数据  
            Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象  
            //获取add()方法  
            Method m = listClass.getMethod("add", Object.class);  
            //调用add()方法  
            m.invoke(strList, 100);  
              
            //遍历集合  
            for(Object obj : strList){  
                System.out.println(obj);  
            }  
        }  
    }  
    

    控制台输出:

    aaa
    bbb
    100

    更多相关内容
  • Java面向对象三大特性(封装、继承、多态)

    千次阅读 多人点赞 2021-05-19 20:59:41
    面向对象的语言有三大特性:封装、继承、多态。三大特性是面向对象编程的核心。下面就来介绍一下面向对象的三大特性。 如果想了解面向对象可以看一下这一篇博客类和对象 一、封装 1.封装的概念 在我们写代码的时候...


    前言

    OOP 语言:也就是面向对象编程。
    面向对象的语言有三大特性:封装、继承、多态。三大特性是面向对象编程的核心。下面就来介绍一下面向对象的三大特性。
    如果想了解面向对象可以看一下这一篇博客类和对象


    一、封装

    1. 封装的概念

    在我们写代码的时候经常会涉及两种角色: 类的实现者和类的调用者

    封装的本质就是让类的调用者不必太多的了解类的实现者是如何实现类的,
    把属性和动作隐藏,只提供相应的方法来调用即可,只要知道如何使用类就行了.
    当类的实现者把内部的逻辑发生变化时,类的调用者根本不用因此而修改方法。
    这样就降低了类使用者的学习和使用成本,从而降低了复杂程度,也保证了代码的安全性

    2. private实现封装

    private 访问限制修饰符,被它修饰的字段或者方法就只能在当前类中使用。

    如果我们直接使用public修饰字段

    class People{
        public String name;
        public int age;
    }
    public class Test {
        public static void main(String[] args) {
            People people = new People();
            people.name = "小明";
            people.age = 18;
            System.out.println("姓名:"+people.name+" 年龄:"+people.age);
        }
    }
    

    运行结果

    在这里插入图片描述
    这样的代码必须要了解 People 这个类的才能类内部的实现, 才能够使用这个类. 学习成本较高。
    而且一旦类的实现者把name这两个字段修改成myName,外部就无法调用了,那么类的调用者就需要大量的修改代码,维护成本就非常高了。

    使用 private 封装属性, 并提供 public 方法供类的调用者使用.

    class People{
        private String name;
        private int age;
        
        public void show() {
            System.out.println("姓名:"+name+" 年龄:"+age);
        }
    }
    public class Test {
        public static void main(String[] args) {
            People people = new People();
            people.show();
        }
    }
    

    此时字段已经使用 private 来修饰. 类的调用者(main方法中)不能直接使用. 而需要借助 show 方法.
    此时类的使用者就不必了解 Person 类的实现细节. 同时如果类的实现者修改了字段的名字,
    类的调用者不需要做出任何修改(类的调用者根本访问不到 name, age这样的字段).

    那么问题来了,我们前面说过 private 修饰的字段只能在当前类中使用。也就是说现在我们访问不到了name和age了。这就得用到 ger 和 set 方法了

    3. getter和setter方法

    当我们用private修饰字段后,这个字段就无法被直接使用了。
    这个时候就用到了 get 和 set 方法了

    代码示例:

    class People{
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public void show() {
            System.out.println("姓名:"+name+" 年龄:"+age);
        }
    }
    public class Test {
        public static void main(String[] args) {
            People people = new People();
            people.setName("小明");
            people.setAge(18);
            people.show();
        }
    }
    

    运行结果

    在这里插入图片描述

    getName 即为 getter 方法, 表示获取这个成员的值.
    setName 即为 setter 方法, 表示设置这个成员的值
    不是所有的字段都一定要提供 setter / getter 方法, 而是要根据实际情况决定提供哪种方法.

    在 IDEA中快速生成 get 和 set 方法
    Alt+Insert 键或者点鼠标右建找到Generate
    在这里插入图片描述

    4.封装的好处

    1.提高了数据的安全性
    别人不能够通过 变量名来修改某个私有的成员属性
    2.操作简单
    封装后,类的调用者在使用的时候,只需调用方法即可。
    3.隐藏了实现
    实现过程对类的调用者是不可见的,类的调用者只需调用方法即可,不知道具体实现。

    二、继承

    1. 继承的概念

    继承的意义:代码的重复使用

    代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法).
    有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联。

    来看一段代码:

    class Animal {
        public String name;
    
        public void eat(String food) {
            System.out.println(this.name + "正在吃" + food);
        }
    }
    class Dog {
        public String name;
            
        public void eat() {
            System.out.println(this.name+"吃东西");
        }
    }
    class Bird {
        public String name;
        
        public void eat() {
            System.out.println(this.name+"吃东西");
        }
        
        public void fly() {
            System.out.println(this.name+"起飞");
        }
    }
    

    这个代码我们发现其中存在了大量的冗余代码.
    仔细分析, 我们发现 Animal 和 Cat 以及 Bird 这几个类中存在一定的关联关系。

    • 这三个类都有相同的eat方法
    • 这三个类都有一个name属性
    • 从逻辑上讲, Cat 和 Bird 都是一种 Animal (is - a语义).

    此时我们就可以让 Cat 和 Bird 分别继承 Animal 类, 来达到代码重用的效果

    2. extends实现继承

    基本语法

    class 子类 extends 父类 {
    
    } 
    
    • 使用 extends 指定父类.
    • Java不同于C++/Python,JAVA中一个子类只能继承一个父类(单继承)
    • 子类会继承父类的所有public 的字段和方法.
    • 对于父类的 private 的字段和方法, 子类中是无法访问的.
    • 子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用

    我们再把上面的代码修改一下,用extends关键字实现继承,此时我们让 Cat 和 Bird 继承自 Animal 类, 那么 Cat 在定义的时候就不必再写 name 字段和 eat 方法。

    class Animal {
        public String name;
    
    
        public void eat() {
            System.out.println(this.name + " 正在吃");
        }
    }
    class Dog extends Animal {
    
    }
    class Bird extends Animal{
    
        public void fly() {
            System.out.println(this.name+"起飞");
        }
    }
    public class Test {
        public static void main(String[] args) {
            Dog dog = new Dog();
            dog.name = "金毛";
            dog.eat();
        }
    }
    

    运行结果

    在这里插入图片描述

    此时, Animal 这样被继承的类, 我们称为 父类 , 基类超类, 对于像 Cat 和 Bird 这样的类, 我们称为 子类,或者派生类
    和现实中的儿子继承父亲的财产类似, 子类也会继承父类的字段和方法, 以达到代码重用的效果

    此时我们来简单看一下内存中的存储
    在这里插入图片描述

    3. super 关键字

    我们在类和对象讲过当一个类没有写构造方法的时候,系统默认会有一个没有参数且没有任何内容的构造方法。

    来看一个列子:

    在这里插入图片描述
    当我们自己给父类写了一个构造方法后,两个子类都报错了,是什么原因呢?

    因为当子类继承父类后,在构造子类之前,就必须先帮父类进行构造。(重点)

    就用到了关键字super
    super 表示获取到父类实例的引用.,和this类似共有三种用法

    1.super.父类的成员变量
    2.super.父类的成员方法
    3.super():调用父类的构造方法

    注意:super 和 this一样不能在静态方法里使用 !

    class Animal {
        public String name;
        public Animal(String name) {
            this.name = name;
        }
        public void eat() {
            System.out.println(this.name + " 正在吃");
        }
    }
    class Bird extends Animal{
        public String name = "乌鸦";
        public Bird(String name) {
            super(name);// 使用 super 调用父类的构造方法
        }
        public void fly() {
            System.out.println(super.name);//调用父类的成员变量
            super.eat();//调用父类的构造方法
            System.out.println(this.name+"起飞");//调用自己的成员变量
        }
    }
    public class Test {
        public static void main(String[] args) {
            Bird bird = new Bird("麻雀");
            bird.fly();
        }
    }
    

    运行结果

    在这里插入图片描述
    当子类和父类有了同名的成员变量的内存结够图

    在这里插入图片描述
    注意:在用super关键字在子类的构造方法里帮父类构造的时候一定要在第一行
    在这里插入图片描述

    Object

    如果一个类没有指定父类的时候,默认继承的就是Object类。

    class Animal {//默认继承Object类
        public String name;
        public Animal(String name) {
            this.name = name;
        }
        public void eat() {
            System.out.println(this.name + " 正在吃");
        }
    }
    

    4.访问权限

    (1) private

    当我们把父类的访问权限改成 private 的时候,子类就无法访问了。但并不是没有继承,而是无法直接访问了,因为被 private 修饰的只能在当前类里使用!

    在这里插入图片描述
    private是可以修饰构造方法的,在类外不能实例化对象,要提供一个静态方法来帮助构造一个对象。这样的操作在以后的单例设计模式会用到。

    在这里插入图片描述

    (2) protected

    刚才我们发现, 如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 “封装” 的初衷.两全其美的办法就是 protected 关键字

    • 对于类的调用者来说, protected 修饰的字段和方法是不能访问的
    • 对于类的 子类 和 同一个包的其他类 来说, protected修饰的字段和方法是可以访问的

    在这里插入图片描述

    在这里插入图片描述

    (3) default

    当一个类什么修饰符都不加的时候就是默认的访问权限,也就是包访问权限default .相当于这个类只能在当前包中使用。

    class Cat extends Animal{//没有任何访问权限修饰符
        Cat(String name) {
            super(name);
            this.name = name;
        }
    }
    

    (4) 小结

    总结: Java 中对于字段和方法共有四种访问权限

    1.private: 类内部能访问, 类外部不能访问
    2.默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
    3.protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
    4.public : 类内部和类的调用者都能访问

    在这里插入图片描述

    5.更复杂的继承

    这样的继承方式称为多层继承, 即子类还可以进一步的再派生出新的子类.
    虽然语法上可以继承很多层,但不建议超过三层,超过三层的话就用final修饰最后一层,如果再往下继承的话编译器就会报错。

    class Animal {
        public String name;
        public void eat() {
            System.out.println(this.name + " 正在吃");
        }
    }
    class B extends Animal {
    
    }
    class C extends B {
    
    }
    final class D extends C {
    
    }
    

    6.final 关键字

    1.final修饰变量(常量,这个常量不能再被修改)
    2.final修饰类,密封类:当前类不能再继承
    3.final修饰方法,密封方法:该方法不能进行重写

    三、组合

    和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果.
    例如表示一个学校:

    public class Student {
    
    }
    public class Teacher {
    
    }
    public class School {
     public Student[] students;
     public Teacher[] teachers;
    } 
    

    组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段.
    这是我们设计类的一种常用方式之一.

    组合表示 has - a 语义 在刚才的例子中, 我们可以理解成一个学校中 “包含” 若干学生和教师.

    继承表示 is - a 语义 在上面的 “动物和猫” 的例子中, 我们可以理解成一只猫也 “是” 一种动物

    一定要理解组合和继承的区别

    四、多态

    1. 向上转型

    (1) 概念

    向上转型就是把一个子类引用给一个父类引用,也就是父类引用 引用了子类的对象

    class Animal {
        public String name;
        public void eat() {
            System.out.println(this.name + " 正在吃");
        }
    }
    class Cat extends Animal {
        
    }
    public class Test extends TestDemo {
    
        public static void main(String[] args) {
            //父类引用 引用了 子类引用所引用的对象
            Cat cat = new Cat();
            Animal animal = cat;//向上转型
        }
    }
    

    我们把一个 Animal类型引用了它的子类Cat这就是向上转型

    (2) 向上转型发生的几种时机

    1.直接赋值

    public static void main(String[] args) {
            //父类引用 引用了 子类引用所引用的对象
            Animal animal = new Cat();;//向上转型
    }
    

    2.方法传参

    我们这里把一个 Cat的子类 传给 一个Animal类型的父类,这里也是能发生向上转型的

    public class Test extends TestDemo {
    
        public static void func(Animal animal) {
            
        }
        public static void main(String[] args) {
            //父类引用 引用了 子类引用所引用的对象
            Cat cat = new Cat();
            func(cat);
        }
    }
    

    3.方法返回

    这里func方法的返回类型是 Animal 但返回的确是一个Cat类型,这里也是发生了向上转型

    public class Test extends TestDemo {
        public static Animal func() {
            Cat cat = new Cat();
            return cat;
        }
        public static void main(String[] args) {
            Animal animal = func();
        }
    }
    

    (3) 注意事项

    注意:当发生向上转型的时候,通过父类引用只能调用父类的自己的方法和成员变量

    在这里插入图片描述

    2.向下转型

    (1) 概念

    知道了向上转型,那么向下转型就好理解了。向下转型就是父类对象转成子类对象。

    我们把一个父类引用 Animal类型的引用 给了一个 Bird类型 的引用,这就是向下转型

    注意:向下转型的时候一定要进行强制类型转换

    class Animal {
        public String name;
        public void eat() {
            System.out.println(this.name + " 正在吃");
        }
    }
    class Cat extends Animal {
    
    }
    class Bird extends Animal {
        public int age;
        public void fly() {
            System.out.println(this.name+"起飞");
        }
    }
    public class Test extends TestDemo {
        public static void main(String[] args) {
            Animal animal = new Animal();
            Bird bird = (Bird) animal;//必须进行强制类型转换
        }
    }
    

    (2) instanceof关键字

    向下转型我们一般不建议使用,因为它非常不安全。

    来看一段代码:

    在这里插入图片描述
    运行结果:

    在这里插入图片描述
    运行之前并没有报出,但运行之后这里报出了一个类型转换异常。

    因为这里Animal本身引用的就是一个Cat的对象,然后把它强转为Bird,因为Cat里根本没有fly()方法,就相当于你让一只猫去飞,它能非起来吗?

    所以向下转型非常的不安全,如果要让它安全就要加上一个关键字instanceof 来判断一下。

    public class Test extends TestDemo {
        public static void main(String[] args) {
            Animal animal = new Bird();
            if (animal instanceof Bird) {
                Bird bird = (Bird) animal;
                bird.fly();
            }
        }
    }
    

    instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全了

    所以向下转型我们一般不建议使用,如果非要使用就一定要用instanceof 关键字判断一下。

    3.动态绑定(运行时绑定)

    (1) 动态绑定概念

    动态绑定发生的前提

    1.先向上转型
    2.通过父类引用来调用父类和子类同名的覆盖方法

    来看一段代码:

    在这里插入图片描述
    运行结果

    在这里插入图片描述

    我们发现这里我们通过父类引用调用了 Animal 和 Cat
    同名的覆盖方法(重写),运行的是子类Cat的eat方法。此时这里就发生了动态绑定

    动态绑定也就叫运行时绑定,因为程序在编译的时候调用的其实是父类的 eat 方法,但是程序在运行时运行的则是子类的 eat 方法,运行期间发生了绑定。

    (2) 重写(Override)

    前面的博客中我们提到了重载,那么重写又是什么时候发生的呢?

    重写发生的条件

    1.方法名相同
    2.方法的参数列表相同(返回类型和数据类型)
    3.方法的返回值相同

    返回值构成父子类关系也是可以发生重写的,此时叫做:协变类型

    在这里插入图片描述

    注意:

    1.子类的重写的这个方法,他的访问修饰符,一定要大于等于父类方法的访问修饰符
    2.被final和static修饰的方法是不能发生重写的

    在这里插入图片描述

    (3) @Override注解

    被@Override注解修饰的方法代表是要重写的方法,一但方法被这个注解修饰,只要方法的方法名、放回值、参数列表有一个地方不满足重写的要求,编译器就会报错。

    这个注解可以帮助开发人员进行检查

    在这里插入图片描述

    (4) 动态绑定的一个坑

    来看一段代码,我们实例化一个Cat类,因为Cat是子类,所以要帮父类先构造,那么在父类的构造方法里有一个 eag 方法,那么会执行哪个类里的 eag 方法呢?

    在这里插入图片描述
    运行结果

    在这里插入图片描述
    我们发现这里调用的不是 是Animal 的 eat 方法,而是 Cat 的,因为这里也发生了动态绑定。

    所以构造方法当中也是可以发生动态绑定的
    注意:这样的代码以后不要轻易写出来!

    4.多态

    (1) 理解多态

    多态其实就是一种思想,一个事物表现出不同的形态,就是多态。

    通过代码来理解,这里我要打印一些形状

    class Shape {
        public void draw() {
    
        }
    }
    
    class Rect extends Shape{
    
        public void draw() {
            System.out.println("♦");
        }
    }
    
    class Cycle extends Shape{
        public void draw() {
            System.out.println("●");
        }
    }
    
    class Flower extends Shape{
        public void draw() {
            System.out.println("❀");
        }
    }
    
    class Triangle extends Shape{
        public void draw() {
            System.out.println("△");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Shape shape = new Rect();
            shape.draw();
            Shape shape1 = new Cycle();
            shape1.draw();
            Shape shape2 = new Flower();
            shape2.draw();
            Shape shape3 = new Triangle();
            shape3.draw();
        }
    }
    

    运行结果

    在这里插入图片描述
    这不就是动态绑定吗?和多态有什么关系吗?
    当我们在这个代码中添加一个drawMap方法后
    在这里插入图片描述
    运行结果

    在这里插入图片描述
    这不就是动态绑定吗?

    我们细看会发现这是同样一个引用调用同样一个方法,能表现出不同的形态,这不就是多态思想?其实多态用到的就是动态绑定。

    在这个代码中, 前面的代码是 类的实现者 编写的, Test这个类的代码是 类的调用者 编写的.

    当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当 前的shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape
    对应的实例相关), 这种行为就称为 多态

    (2) 多态的好处

    1.类调用者对类的使用成本进一步降低.
    封装是让类的调用者不需要知道类的实现细节.
    多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可
    2. 可扩展能力更强
    如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低
    对于类的调用者来说(drawShapes方法), 只要创建一个新类的实例就可以了, 改动成本很低


    总结

    1.封装:安全性
    2.继承:为了代码的复用(java是单继承)
    3.多态:一个事物表现出不同的形态
    4.注意重载和重写的区别
    5.注意this和super的区别

    展开全文
  • Java面向对象 - 封装、继承和多态

    千次阅读 多人点赞 2021-01-16 10:39:55
    第1关:什么是封装,如何使用封装 ...对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象就可以完成自己的操作。使用封装有四大好处: 良好的封装能够减少耦合。 类内部的结构可以

    第1关:什么是封装,如何使用封装
    任务描述
    本关任务:构造一个类,把对象的属性封装起来,同时提供一些可以被外界访问属性的方法。
    相关知识
    为了完成本关任务,你需要掌握:1.什么是封装;2.封装的意义;3.实现Java封装的步骤。
    什么是封装
    封装:就是隐藏对象的属性和实现细节,仅对外提供公共访问方式。
    封装时的权限控制符区别如下:
    在这里插入图片描述
    封装的意义
    对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象就可以完成自己的操作。使用封装有四大好处:
    良好的封装能够减少耦合。
    类内部的结构可以自由修改。
    可以对成员进行更精确的控制。
    隐藏信息,实现细节。
    封装把一个对象的属性私有化,同时提供一些可以被外界访问属性的方法,如果不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
    实现Java封装的步骤
    修改属性的可见性来限制对属性的访问(一般限制为private),例如:

    public class Person {
     private String name;
     private int age;
    }
    

    这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
    对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:

    /*
    *封装演示
    */
    public class Person {
        /*
         * 对属性的封装 一个人的姓名、性别和年龄都是这个人的私有属性
         */
        private String name;
        private String sex;
        private int age;
        /*
         * setter()、getter()是该对象对外开放的接口
         */
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = sex;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    

    采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
    封装可以使我们容易地修改类的内部实现,而无需修改使用了该类的客户代码,就可以对成员变量进行更精确的控制。

    public void setAge(int age) {
        if (age > 120) {
            System.out.println("ERROR:error age input...."); // 提示错误信息
        } else {
            this.age = age;
        }
    }
    public String getSexName() {
        if ("0".equals(sex)) {
            sexName = "女";
        } else if ("1".equals(sex)) {
            sexName = "男";
        } else {
            sexName = "人妖";
        }
        return sexName;
    }
    

    编程要求
    根据提示,在右侧编辑器Begin-End处补充代码:
    声明一个Person类,私有化属性name和age,并将字段封装起来;
    在Person类中定义一个talk()方法,打印姓名和年龄信息;
    在main方法中声明并实例化一Person对象p,给p中的属性赋值,调用talk()方法打印 我是:张三,今年:18岁。
    测试说明
    测试输入: 无
    预期输出:
    我是:张三,今年:18岁
    开始你的任务吧,祝你成功!
    参考代码:

    package case1;
    public class TestPersonDemo {
    	public static void main(String[] args) {
    		/********* begin *********/
    		// 声明并实例化一Person对象p
          Person p=new Person();
    		// 给p中的属性赋值
                 String name=p.getName();
    			 name="张三";
    			 p.setName(name);
    			 int age=p.getAge();
    			 age=18;
    			 p.setAge(age);
    		// 调用Person类中的talk()方法
                 p.talk();
    		/********* end *********/
    
    	}
    }
    // 在这里定义Person类
    class Person {
    	/********* begin *********/
             private String name;
    		 private int age;
    		 public String getName(){
    			 return name;
    		 }
    		 public void setName(String name){
    			 this.name=name;
    		 }
    		 public int getAge(){
    			 return age;
    		 }
    		 public void setAge(int age){
    			this.age=age;
    		 }
    		 public void talk(){
    			 System.out.print("我是:"+name+",今年:"+age+"岁");
    		 }
    	/********* end *********/
    }
    

    第2关:什么是继承,怎样使用继承
    任务描述
    本关任务:掌握继承的基本概念以及怎么使用继承。
    相关知识
    为了完成本关任务,你需要掌握:1.继承的基本概念;2.继承的特性;3.子类对象的实例化过程。
    继承的基本概念
    所谓继承:是指可以让某个类型的对象获得另一个类型的对象的属性的方法。
    在这里插入图片描述
    兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
    食草动物和食肉动物又是属于动物类。
    所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
    虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
    在讲解继承的基本概念之前,读者可以先想一想这样一个问题:现在假设有一个Person类,里面有name与age两个属性,而另外一个Student类,需要有name、age、school三个属性,如图所示,从这里可以发现Person中已经存在有name和age两个属性,所以不希望在Student类中再重新声明这两个属性,这个时候就需要考虑是不是可以将Person类中的内容继续保留到Student类中,也就是引出了接下来所要介绍的类的继承概念。
    在这里插入图片描述
    在这里希望Student类能够将 Person类的内容继承下来后继续使用:
    在这里插入图片描述
    Java类的继承,可用下面的语法来表示:

    class 父类 // 定义父类
    {
        ...
    }
    class 子类 extends 父类 // 用extends关键字实现类的继承
    {
        ...
    }
    

    范例:

    public class TestPersonStudentDemo {
        public static void main(String[] args) {
            Student s = new Student();
            // 访问Person类中的name属性
            s.name = "张三";
            // 访问Person类中的age属性
            s.age = 18;
            // 访问Student类中的school属性
            s.school = "哈佛大学";
            System.out.println("姓名:" + s.name + ",年龄:" + s.age + ",学校:" + s.school);
        }
    }
    class Person {
        String name;
        int age;
    }
    class Student extends Person {
        String school;
    }
    

    输出结果:
    姓名:张三,年龄:18,学校:哈佛大学
    由上面的程序可以发现,在Student类中虽然并未定义name与age属性,但在程序外部却依然可以调用name或age,这是因为Student类直接继承自Person类,也就是说Student类直接继承了Person类中的属性,所以Student类的对象才可以访问到父类中的成员。
    在这里插入图片描述
    继承的特性
    子类拥有父类非private的属性和方法;
    子类可以拥有自己的属性和方法,即子类可以对父类进行扩展;
    子类可以用自己的方式实现父类的方法;
    在Java中只允许单继承,而不允许多重继承,也就是说一个子类只能有一个父类,但是Java中却允许多层继承,多层继承就是,例如类C继承类B,类B继承类A,所以按照关系就是类A是类B的父类,类B是类C的父类,这是Java继承区别于C++继承的一个特性;
    提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
    多重继承:
    在这里插入图片描述

    class A{
        ...
    }
    class B{
        ...
    }
    class C extends A,B{
        ...
    }
    

    由上面可以发现类C同时继承了类A与类B,也就是说类C同时继承了两个父类,这在Java中是不允许的。
    多层继承:
    在这里插入图片描述

    class A{
        ...
    }
    class B extends A{
        ...
    }
    class C extends B{
        ...
    }
    

    由上面可以发现类B继承了类A,而类C又继承了类B,也就是说类B是类A的子类,而类C则是类A的孙子类。
    子类对象的实例化过程
    既然子类可以继承直接父类中的方法与属性,那父类中的构造方法呢?请看下面的范例:

    public class TestPersonStudentDemo1 {
        public static void main(String[] args) {
            Student s = new Student();
        }
    }
    class Person {
        String name;
        int age;
        // 父类的构造方法
        public Person() {
            System.out.println("1.public Person(){}");
        }
    }
    class Student extends Person {
        String school;
        // 子类的构造方法
        public Student() {
            System.out.println("2.public Student(){}");
        }
    }
    

    输出结果:
    1.public Person(){}
    2.public Student(){}
    从程序输出结果中可以发现,虽然程序第3行实例化的是子类的对象,但是程序却先去调用父类中的无参构造方法,之后再调用了子类本身的构造方法。所以由此可以得出结论,子类对象在实例化时会默认先去调用父类中的无参构造方法,之后再调用本类中的相应构造方法。
    实际上在本范例中,在子类构造方法的第一行默认隐含了一个super()语句,上面的程序如果改写成下面的形式,也是可以的:

    class Student extends Person{
        String school ;
        // 子类的构造方法
        public Student(){
            super() ; //实际上在程序的这里隐含了这样一条语句
            System.out.println("2.public Student(){}");
        }
    }
    

    继承条件下构造方法调用规则如下:
    如果子类的构造方法中没有通过super显示调用父类的有参构造方法,也没有通过this显示调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下写不写super()语句效果都是一样;
    如果子类的构造方法中通过super显示调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法;
    如果子类的构造方法中通过this显示调用自身的其他构造方法,在相应构造方法中应用以上两条规则;
    特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
    编程要求
    根据提示,在右侧编辑器Begin-End处补充代码:
    声明一个Animal类,将属性name和age封装起来,提供对外的公共访问方法;
    声明一个Cat类和Dog类,都继承Animal类,分别定义各自的voice方法和eat方法;
    在main方法中分别实例化一个Cat对象和Dog对象,设置各自的属性并调用这两个方法,再打印出名字和年龄信息;
    具体具体输出要求请看测试说明。
    测试说明
    测试输入:无
    预期输出:
    大花猫喵喵叫
    大花猫吃鱼
    大花猫6岁
    大黑狗汪汪叫
    大黑狗吃骨头
    大黑狗8岁
    开始你的任务吧,祝你成功!
    参考代码:

    package case2;
    public class extendsTest {
    	public static void main(String args[]) {
    		// 实例化一个Cat对象,设置属性name和age,调用voice()和eat()方法,再打印出名字和年龄信息
    		/********* begin *********/
                  Cat c=new Cat("大花猫",6);
    			  c.voice();
    			  c.eat();
    			  c.show();
    		/********* end *********/
    		// 实例化一个Dog对象,设置属性name和age,调用voice()和eat()方法,再打印出名字和年龄信息
    		/********* begin *********/
                 Dog d=new Dog("大黑狗",8);
    			 d.voice();
    			 d.eat();
    			 d.show();
    		/********* end *********/
    	}
    }
    class Animal {
    	/********* begin *********/
               String name;
    		   int age;
    	/********* end *********/
    }
    class Cat extends Animal {
    	// 定义Cat类的voice()和eat()方法
    	/********* begin *********/
              Cat(String name,int age){
    			  this.name=name;
    			  this.age=age;
    		  }
    			  void voice(){
    				  System.out.println(name+"喵喵叫");
    			  }
    			  void eat(){
    				  System.out.println(name+"吃鱼");
    			  }
    			  void show(){
    				  System.out.println(name+age+"岁");
    			  }
     	  
    	/********* end *********/
    }
    class Dog extends Animal {
    	// 定义Dog类的voice()和eat()方法
    	/********* begin *********/
              Dog(String name,int age){
    			  this.name=name;
    			  this.age=age;
    		  }
    			  void voice(){
    				  System.out.println(name+"汪汪叫");
    			  }
    			  void eat(){
    				  System.out.println(name+"吃骨头");
    			  }
    			  void show(){
    				  System.out.println(name+age+"岁");
    			  }
    	/********* end *********/
    }
    

    第3关:super关键字的使用
    任务描述
    本关任务:掌握super关键字的使用。
    相关知识
    为了完成本关任务,你需要掌握:1.super关键字;2.super关键字的使用;3.super与this关键字的比较。
    super关键字
    在上一节中曾经提到过super的使用,那super到底是什么呢?super关键字出现在子类中,我们new子类的实例对象的时候,子类对象里面会有一个父类对象。怎么去引用里面的父类对象呢?使用super来引用,所以可以得出结论:super主要的功能是完成子类调用父类中的内容,也就是调用父类中的属性或方法。
    super关键字的使用
    super关键字的用法如下:
    super可以用来引用直接父类的实例变量。
    super可以用来调用直接父类方法。
    super()可以用于调用直接父类构造函数。
    1.super用于引用直接父类实例变量

    public class TestSuper1 {
        public static void main(String args[]) {
            Dog d = new Dog();
            d.printColor();
        }
    }
    class Animal {
        String color = "white";
    }
    class Dog extends Animal {
        String color = "black";
        void printColor() {
            System.out.println(color);// prints color of Dog class
            System.out.println(super.color);// prints color of Animal class
        }
    }
    

    输出结果:
    black
    white
    在上面的例子中,Animal和Dog都有一个共同的属性:color。 如果我们打印color属性,它将默认打印当前类的颜色。要访问父属性,需要使用super关键字指定。
    2.通过super来调用父类方法

    public class TestSuper2 {
        public static void main(String args[]) {
            Dog d = new Dog();
            d.work();
        }
    }
    class Animal {
        void eat() {
            System.out.println("eating...");
        }
    }
    class Dog extends Animal {
        void eat() {
            System.out.println("eating bread...");
        }
        void bark() {
            System.out.println("barking...");
        }
        void work() {
            super.eat();
            bark();
        }
    }
    

    输出结果:
    eating…
    barking…
    在上面的例子中,Animal和Dog两个类都有eat()方法,如果要调用Dog类中的eat()方法,它将默认调用Dog类的eat()方法,因为当前类的优先级比父类的高。所以要调用父类方法,需要使用super关键字指定。
    3.使用super来调用父类构造函数

    public class TestSuper3 {
        public static void main(String args[]) {
            Dog d = new Dog();
        }
    }
    class Animal {
        Animal() {
            System.out.println("animal is created");
        }
    }
    class Dog extends Animal {
        Dog() {
            super();
            System.out.println("dog is created");
        }
    }
    

    输出结果:
    animal is created
    dog is created
    注意:如果没有使用super()或this(),则super()在每个类构造函数中由编译器自动添加。
    super与this关键字的比较
    super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
    this关键字:指向自己的引用。
    范例:

    public class TestAnimalDogDemo {
        public static void main(String[] args) {
            Animal a = new Animal();
            a.eat();
            Dog d = new Dog();
            d.eatTest();
        }
    }
    class Animal {
        void eat() {
            System.out.println("animal : eat");
        }
    }
    class Dog extends Animal {
        void eat() {
            System.out.println("dog : eat");
        }
        void eatTest() {
            this.eat(); // this 调用自己的方法
            super.eat(); // super 调用父类方法
        }
    }
    

    输出结果:
    animal : eat
    dog : eat
    animal : eat
    在这里插入图片描述
    上表对 this 与 super 的差别进行了比较,从上表中不难发现,用 super 或 this 调用构造方法时都需要放在首行,所以super 与 this 调用构造方法的操作是不能同时出现的。
    编程要求
    comment: <> (“编程要求”部分介绍本关任务的具体要求,如实现步骤,规则等,最好能给出效果图)
    根据提示,在右侧编辑器Begin-End处补充代码:
    声明一个名为Person的类,里面有name与age两个属性,并声明一个含有两个参数的构造方法;
    声明一个名为Student的类,此类继承自Person类,添加一个属性school,在子类的构造方法中调用父类中有两个参数的构造方法;
    实例化一个Student类的对象s,为Student对象s中的school赋值,打印输出姓名:张三,年龄:18,学校:哈佛大学。
    测试说明
    测试输入:无
    预期输出:
    姓名:张三,年龄:18,学校:哈佛大学
    开始你的任务吧,祝你成功!
    参考代码:

    package case3;
    public class superTest {
    	public static void main(String[] args) {
    		// 实例化一个Student类的对象s,为Student对象s中的school赋值,打印输出信息
    		/********* begin *********/
        Student s=new Student();
    	s.school="哈佛大学";
    	System.out.println("姓名:"+s.name+",年龄:"+s.age+",学校:"+s.school);
    		/********* end *********/
    	}
    }
    class Person {
    	/********* begin *********/
         String name;
    	 int age;
         public Person(String name,int age){
             this.name=name;
             this.age=age;
         }
    	/********* end *********/
    }
    class Student extends Person {
    	/********* begin *********/
            String school;
          public Student(){
              super("张三",18);
          }
    	/********* end *********/
    }
    

    第4关:方法的重写与重载
    任务描述
    本关任务:掌握方法的重写与重载。
    相关知识
    为了完成本关任务,你需要掌握:1.方法的重写(override);2.方法的重载(overload);3.重写与重载之间的区别。
    方法的重写(override)
    方法的重写
    子类从父类中继承方法,有时,子类需要修改父类中定义的方法的实现,这称做方法的重写(method overriding)。“重写”的概念与“重载”相似,它们均是Java“多态”的技术之一,所谓“重载”,即是方法名称相同,但却可在不同的场合做不同的事。当一个子类继承一父类,而子类中的方法与父类中的方法的名称、参数个数和类型都完全一致时,就称子类中的这个方法重写了父类中的方法。“重写”又称为“复写”、“覆盖”。
    如何使用重写

    class Super {
     访问权限 方法返回值类型 方法1(参数1) {
         ...
     }
    }
    class Sub extends Super{
     访问权限 方法返回值类型 方法1(参数1) —————>复写父类中的方法
     {
         ...
     }
    }
    

    注意:方法重写时必须遵循两个原则,否则编译器会指出程序出错。
    重写的方法不能比被重写的方法有更严格的访问权限;
    重写的方法不能比被重写的方法产生更多的异常(关于异常,在后面会介绍)。
    编译器加上这两个限定,是为了与Java语言的多态性(关于方法重写引起的运行时多态,在后面会详细讲述)特点一致而做出的。这样限定是出于对程序健壮性的考虑,为了避免程序执行过程中产生访问权限冲突或有应该捕获而未捕获的异常产生。
    方法的重载(overload)
    1.方法的重载
    首先回顾一下前面所讲的方法的重载,方法重载是指多个方法可以享有相同的名字,但是参数的数量或类型不能完全相同。
    调用方法时,编译器根据参数的个数和类型来决定当前所使用的方法。方法重载为程序的编写带来方便,是OOP多态性的具体变现。在Java系统的类库中,对许多重要的方法进行重载,为用户使用这些方法提供了方便。
    2.重载的规则
    被重载的方法必须改变参数列表(参数个数或类型不一样);
    被重载的方法可以改变返回类型;
    被重载的方法可以改变访问修饰符;
    被重载的方法可以声明新的或更广的检查异常;
    方法能够在同一个类中或者在一个子类中被重载。
    无法以返回值类型作为重载函数的区分标准。
    3.重写与重载之间的区别
    在这里插入图片描述
    方法的重写和重载是Java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
    方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载;
    方法重写是在子类存在方法与父类的方法的名字相同而且参数的个数与类型一样,返回值也一样的方法,就称为方法的重写;
    方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
    在这里插入图片描述
    在这里插入图片描述
    编程要求
    根据提示,在右侧编辑器补充代码。
    声明一个名为Person的类,里面声明name与age两个属性,定义talk()方法返回姓名和年龄信息;
    声明一个名为Student的类,此类继承自Person类,添加school属性,声明带三个参数的构造方法,复写talk()方法,在该方法中调用父类的talk()方法,返回姓名、年龄和学校信息;
    实例化子类对象s,调用talk()方法打印我是:张三,今年:18岁,我在哈佛大学上学。
    测试说明
    测试输入: 无
    预期输出:
    我是:张三,今年:18岁,我在哈佛大学上学
    开始你的任务吧,祝你成功!
    参考代码:

    package case4;
    public class overridingTest {
    	public static void main(String[] args) {
    		// 实例化子类对象s,调用talk()方法打印信息
    		/********* begin *********/
                 Student s=new Student("张三",18,"哈佛大学");
    			 s.talk();
    		/********* end *********/	
    	}
    }
    class Person {
    	/********* begin *********/
        public String name;
    	public int age;
    	 void talk(){
            System.out.println("我是:"+name+",今年:"+age+"岁");
    	 }
    	/********* end *********/
    }
    class Student extends Person {
    	/********* begin *********/
           private String school;
    	   public Student(String name,int age,String school){
    			  this.name=name;
    			  this.age=age;
    			  this.school=school;
    		  }
    		  void talk(){
    			  System.out.print("我是:"+name+",今年:"+age+"岁,我在"+school+"上学");
    		  }
    	/********* end *********/
    }
    

    第5关:抽象类
    任务描述
    本关任务:掌握抽象类的定义及用法。
    相关知识
    为了完成本关任务,你需要掌握:1.抽象类的概念;2.如何定义抽象类。
    抽象类的概念
    前面对类的继承进行了初步的讲解。通过继承,可以从原有的类派生出新的类。原有的类称为基类或父类,而新的类则称为派生类或子类。通过这种机制,派生出的新的类不仅可以保留原有的类的功能,而且还可以拥有更多的功能。
    除了上述的机制之外,Java也可以创建一种类专门用来当作父类,这种类称为“抽象类”。抽象类的作用有点类似“模版”,其目的是要设计者依据它的格式来修改并创建新的类。但是并不能直接由抽象类创建对象,只能通过抽象类派生出新的类,再由它来创建对象。
    如何定义抽象类
    抽象类的定义规则:
    抽象类和抽象方法都必须用abstract关键字来修饰;
    抽象类不能被实例化,也就是不能用new关键字去产生对象;
    抽象方法只需声明,而不需实现;
    含有抽象方法的类必须被声明为抽象类,抽象类的子类必须复写所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
    抽象类的定义格式:

    abstract class 类名称 // 定义抽象类
    {
        声明数据成员;
        访问权限 返回值的数据类型 方法名称(参数...)//定义一般方法
        {
            ...
        }
        abstract 返回值的数据类型 方法名称(参数...);
        //定义抽象方法,在抽象方法里,没有定义方法体
    }
    

    注意:
    在抽象类定义的语法中,方法的定义可分为两种:一种是一般的方法,它和先前介绍过的方法没有什么两样;另一种是“抽象方法”,它是以abstract关键字为开头的方法,此方法只声明了返回值的数据类型、方法名称与所需的参数,但没有定义方法体。
    抽象类也可以像普通类一样,有构造方法、一般方法、属性,更重要的是还可以有一些抽象方法,留给子类去实现,而且在抽象类中声明构造方法后,在子类中必须明确调用。
    编程要求
    根据提示,在右侧编辑器补充代码:
    声明一个名为Person的抽象类,在Person类中声明了三个属性name、age和occupation和一个抽象方法talk();
    声明一个Student类和一个Worker类,都继承自Person类,添加带有三个参数的构造方法;
    分别实例化Student类与Worker类的对象,分别调用各自类中被复写的talk()方法打印输出信息;
    具体输出要求请看测试说明。
    测试说明
    测试输入: 无
    预期输出:
    学生——>姓名:张三,年龄:20,职业:学生!
    工人——>姓名:李四,年龄:30,职业:工人!
    开始你的任务吧,祝你成功!
    参考代码:

    package case5;
    public class abstractTest {
    	public static void main(String[] args) {
    		/********* begin *********/
    		// 分别实例化Student类与Worker类的对象,并调用各自构造方法初始化类属性。
    		   Student s=new Student("张三",20,"学生");
    		   Worker w=new Worker("李四",30,"工人" );
    		// 分别调用各自类中被复写的talk()方法 打印信息。
    		     s.talk();
    			 w.talk();
    	}
    }
    // 声明一个名为Person的抽象类,在Person中声明了三个属性name age occupation和一个抽象方法——talk()。
    abstract class Person {
    	/********* begin *********/
         String name;
    	 int age;
    	 String occupation;
    	  abstract  void talk();
    	/********* end *********/
    }
    // Student类继承自Person类,添加带三个参数的构造方法,复写talk()方法 返回姓名、年龄和职业信息
    class Student extends Person {
    	/********* begin *********/
         public  Student(String name,int age,String occupation){
                this.name=name;
    		this.age=age;
    		this.occupation=occupation;
    	   }
    	 void talk(){
    	System.out.println("学生——>姓名:"+name+",年龄:"+age+",职业:"+occupation+"!");
    		}
    	/********* end *********/
    }
    // Worker类继承自Person类,添加带三个参数的构造方法,复写talk()方法 返回姓名、年龄和职业信息
    class Worker extends Person {
    	/********* begin *********/
       public Worker(String name,int age,String occupation){
    		this.name=name;
    		this.age=age;
    		this.occupation=occupation;
    	}
    	   void talk(){
    		   System.out.println("工人——>姓名:"+name+",年龄:"+age+",职业:"+occupation+"!");
    	   }
    	/********* end *********/
    
    }
    

    第6关:final关键字的理解与使用
    任务描述
    本关任务:理解并能正确使用final关键字。
    相关知识
    为了完成本关任务,你需要掌握:1.final关键字的使用; 2.final关键字修饰类、成员变量和成员方法。
    final关键字的使用
    在Java中声明类、属性和方法时,可使用关键字final来修饰。
    final标记的类不能被继承;
    final标记的方法不能被子类复写;
    final标记的变量(成员变量或局部变量)即为常量,只能赋值一次。
    final关键字修饰类、成员变量和成员方法
    1.final类
    final用来修饰一个类,意味着该类成为不能被继承的最终类。出于安全性的原因和效率上的考虑,有时候需要防止一个类被继承。例如,Java类库中的String类,它对编译器和解释器的正常运行有着很重要的作用,不能轻易改变它,因此把它修饰为final类,使它不能被继承,这就保证了String类的惟一性。同时,如果你认为一个类的定义己经很完美,不需要再生成它的子类,这时也应把它修饰为final类。
    定义一个final类的格式如下:

    final class ClassName{
        ...
    }
    

    注意:声明为final的类隐含地声明了该类的所有方法为final方法。
    下面的结论是成立的:声明一个类既为abstract,又为final是非法的,因为抽象类必须被子类继承来实现它的抽象方法。下面是一个final类的例子:

    final class A{
        ...
    }
    class B extends A{//错误!不能继承A
        ...
    

    2.final修饰成员变量
    变量被声明为final后,成为常值变量(即常量),一旦被通过某种方式初始化或赋值,即不能再被修改。通常static与final一起使用来指定一个类常量。例如:
    static final int SUNDAY=0;
    把final变量用大写字母和下划线来表示,这是一种编码规定。
    3.final修饰成员方法
    用final修饰的方法为最终方法,不能再被子类重写,可以被重载。
    尽管方法重写是Java非常有力的特征,但有时却需要避免这种情况的发生。为了不允许一个方法被重写,在方法的声明中指定final属性即可。例如:

    class A{
        final void method(){}
    }
    class B extends A{//定义A类的一个子类B
        void method(){}//错误,method()不能被重写
    }
    

    该例中,因为method()方法在A中被声明为final,所以不能在子类B中被重写。如果这样做,将导致编译错误。
    方法被声明为final有时可以提高性能:编译器可以自由地内联调用它们,因为它“知道”它们不会被子类重写。
    编程要求
    根据提示,在右侧编辑器Begin-End处补充代码:
    仔细阅读代码,在右侧编辑器中调整代码使程序能正确编译运行;
    具体输出要求请看测试说明。
    测试说明
    测试输入:无
    预期输出:
    speedlimit=120
    running safely with 100kmph
    running safely with 100kmph
    开始你的任务吧,祝你成功!
    参考代码:

    package case6;
    public class finalTest {
    	public static void main(String args[]) {
    		Bike1 obj = new Bike1();
    		obj.run();
    		Honda honda = new Honda();
    		honda.run();
    		Yamaha yamaha = new Yamaha();
    		yamaha.run();
    	}
    }
    //不可以修改 final 变量的值
    // final方法,不可以重写
     不可以扩展 final 类
    //请在此添加你的代码
        /********** Begin *********/
    	class Bike1 {
    	final int speedlimit = 90;
    	void run() {
    		System.out.println("speedlimit=120");
    	}
    }
    class Bike2 {
    	 void run() {
    		System.out.println("running");
    	}
    }
    class Honda extends Bike2 {
    	void run() { 
    		System.out.println("running safely with 100kmph");
    	}
    }
     class Bike3 {
    }
    class Yamaha extends Bike3 { 
    	void run() {
    		System.out.println("running safely with 100kmph");
    	}
    }
        /********** End **********/
    

    第7关:接口
    任务描述
    本关任务:掌握接口相关的知识。
    相关知识
    为了完成本关任务,你需要掌握:1.接口的定义; 2.接口的实现; 3.接口的扩展。
    接口的定义
    接口(interface)是Java所提供的另一种重要技术,它的结构和抽象类非常相似,也具有数据成员与抽象方法,但它与抽象类又有以下两点不同:
    接口里的数据成员必须初始化,且数据成员均为常量;
    接口里的方法必须全部声明为abstract,也就是说,接口不能像抽象类一样保有一般的方法,而必须全部是“抽象方法”。
    接口定义的语法如下:
    interface 接口名称 // 定义抽象类
    {
    final 数据类型 成员名称 = 常量; //数据成员必须赋初值
    abstract 返回值的数据类型 方法名称(参数…);
    //抽象方法,注意在抽象方法里,没有定义方法主体
    }
    接口与一般类一样,本身也具有数据成员与方法,但数据成员一定要赋初值,且此值将不能再更改,方法也必须是“抽象方法”。也正因为方法必须是抽象方法,而没有一般的方法,所以抽象方法声明的关键字abstract是可以省略的。
    相同的情况也发生在数据成员身上,因数据成员必须赋初值,且此值不能再被更改,所以声明数据成员的关键字final也可省略。事实上只要记得:
    接口里的“抽象方法”只要做声明即可,而不用定义其处理的方式;
    数据成员必须赋初值。
    接口的实现
    在Java中接口是用于实现多继承的一种机制,也是Java设计中最重要的一个环节,每一个由接口实现的类必须在类内部复写接口中的抽象方法,且可自由地使用接口中的常量。
    既然接口里只有抽象方法,它只要声明而不用定义处理方式,于是自然可以联想到接口也没有办法像一般类一样,再用它来创建对象。利用接口打造新的类的过程,称之为接口的实现(implementation)。
    接口实现的语法:
    class 类名称 implements 接口A,接口B //接口的实现
    {

    }
    接口的扩展
    接口是Java实现多继承的一种机制,一个类只能继承一个父类,但如果需要一个类继承多个抽象方法的话,就明显无法实现,所以就出现了接口的概念。一个类只可以继承一个父类,但却可以实现多个接口。
    接口与一般类一样,均可通过扩展的技术来派生出新的接口。原来的接口称为基本接口或父接口,派生出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保留父接口的成员,同时也可加入新的成员以满足实际的需要。
    同样的,接口的扩展(或继承)也是通过关键字extends来实现的。有趣的是,一个接口可以继承多个接口,这点与类的继承有所不同。
    接口扩展的语法:
    interface 子接口名称 extends 父接口1,父接口2…
    {

    }
    编程要求
    根据提示,在右侧编辑器补充代码:
    声明一Person接口,并在里面声明三个常量:name、age、occupation,并分别赋值;
    声明一Student类,此类实现Person接口,并复写Person中的talk()方法;
    实例化一Student的对象s,并调用talk()方法,打印信息;
    具体输出要求请看测试说明。
    测试说明
    测试输入:无
    预期输出:
    学生——>姓名:张三,年龄:18,职业:学生!
    开始你的任务吧,祝你成功!
    参考代码:

    package case7;
    public class interfaceTest {
    	public static void main(String[] args) {
    		// 实例化一Student的对象s,并调用talk()方法,打印信息
    		/********* begin *********/
            Student s=new Student();
    		 System.out.println( s.talk());
    		/********* end *********/
    	}
    }
    // 声明一个Person接口,并在里面声明三个常量:name、age和occupation,并分别赋值,声明一抽象方法talk()
    interface Person {
    	/********* begin *********/
        final  String name="张三";
    	final  int age=18;
    	final  String occupation="学生";
    	public abstract String talk();
    	/********* end *********/
    }
    // Student类继承自Person类 复写talk()方法返回姓名、年龄和职业信息
    class Student implements Person {
    	/********* begin *********/
            public String talk(){
      return   "学生——>姓名:"+this.name+",年龄:"+this.age+",职业:"+this.occupation+"!";
    		}    
    	/********* end *********/
    }
    

    第8关:什么是多态,怎么使用多态
    任务描述
    本关任务:掌握对象的多态性。
    相关知识
    为了完成本关任务,你需要掌握:1.什么是多态;2.多态的实现条件;3.多态的实现形式。
    什么是多态
    所谓多态:就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
    多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
    在这里插入图片描述
    多态性是对象多种表现形式的体现。
    现实中,比如我们按下F1键这个动作:
    如果当前在Flash界面下弹出的就是AS 3的帮助文档;
    如果当前在Word下弹出的就是Word帮助;
    在Windows下弹出的就是Windows帮助和支持。
    同一个事件发生在不同的对象上会产生不同的结果。
    多态的实现条件
    多态的三个条件:
    继承的存在(继承是多态的基础,没有继承就没有多态);
    子类重写父类的方法(多态下调用子类重写的方法);
    父类引用变量指向子类对象(子类到父类的类型转换)。
    子类转换成父类时的规则:
    将一个父类的引用指向一个子类的对象,称为向上转型(upcasting),自动进行类型转换。此时通过父类引用调用的方法是子类覆盖或继承父类的方法,不是父类的方法。 此时通过父类引用变量无法调用子类特有的方法;
    如果父类要调用子类的特有方法就得将一个指向子类对象的父类引用赋给一个子类的引用,称为向下转型,此时必须进行强制类型转换。
    以下是一个多态实例的演示,详细说明请看注释:

    public class TestAnimalDemo {
        public static void main(String[] args) {
            show(new Cat()); // 以 Cat 对象调用 show 方法
            show(new Dog()); // 以 Dog 对象调用 show 方法
            Animal a = new Cat(); // 向上转型
            a.eat(); // 调用的是 Cat 的 eat
            Cat c = (Cat) a; // 向下转型
            c.work(); // 调用的是 Cat 的 work
        }
        public static void show(Animal a) {
            a.eat();
            // 类型判断
            if (a instanceof Cat) { // 猫做的事情
                Cat c = (Cat) a;
                c.work();
            } else if (a instanceof Dog) { // 狗做的事情
                Dog c = (Dog) a;
                c.work();
            }
        }
    }
    abstract class Animal {
        abstract void eat();
    }
    class Cat extends Animal {
        public void eat() {
            System.out.println("吃鱼");
        }
        public void work() {
            System.out.println("抓老鼠");
        }
    }
    class Dog extends Animal {
        public void eat() {
            System.out.println("吃骨头");
        }
        public void work() {
            System.out.println("看家");
        }
    }
    

    输出结果:
    吃鱼
    抓老鼠
    吃骨头
    看家
    吃鱼
    抓老鼠
    可以用 instanceof 判断一个类是否实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。instanceof 的语法格式为:
    对象 instanceof 类(或接口)
    它的返回值是布尔型的,或真(true)、或假(false)。
    #####多态的实现形式
    在Java中有两种形式可以实现多态:继承和接口。
    基于继承实现的多态
    基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。
    基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。
    如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法。
    基于接口实现的多态
    继承是通过重写父类的同一方法的几个不同子类来体现的,那么就可能是通过实现接口并覆盖接口中同一方法的几不同的类体现的。
    在接口的多态中,指向接口的引用必须是指定实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。
    继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。
    编程要求
    根据提示,在右侧编辑器补充代码:
    声明一个Animal类,此类中定义eat()方法;
    声明Dog类、Cat类、Lion类,均继承自Animal类,并复写了eat()方法;
    运用多态方式实例化子类对象并调用eat()方法打印输出信息;
    具体输出要求请看测试说明。
    测试说明
    测试输入:无
    预期输出:
    eating bread…
    eating rat…
    eating meat…
    开始你的任务吧,祝你成功!
    参考代码:

    package case8;
    public class TestPolymorphism {
    	public static void main(String[] args) {
    		// 以多态方式分别实例化子类对象并调用eat()方法
    		/********* begin *********/
               Animal a=new Dog();
    		   a.eat();
    		   Animal b=new Cat();
    		   b.eat();
    		   Animal c=new Lion();
    		   c.eat();
    		/********* end *********/
    	}
    }
    // Animal类中定义eat()方法
      abstract class Animal {
    	/********* begin *********/
         abstract void eat();
    	/********* end *********/
    }
    // Dog类继承Animal类 复写eat()方法
    class Dog extends Animal {
    	/********* begin *********/
          public void eat(){
    		  System.out.println("eating bread...");
    	  }
    	/********* end *********/
    }
    // Cat类继承Animal类 复写eat()方法
    class Cat extends Animal {
    	/********* begin *********/
       public void eat(){
    	   System.out.println("eating rat...");
       }
    	/********* end *********/
    }
    // Lion类继承Animal类 复写eat()方法
    class Lion extends Animal {
    	/********* begin *********/
       public void eat(){
    	   System.out.println("eating meat...");
       }
    	/********* end *********/
    }
    
    展开全文
  • Java面向对象篇:封装、继承、多态

    千次阅读 2022-03-27 21:24:52
    类是对象的数据类型,类是具有相同属性和行为的一组对象的集合 简单理解:类就是对现实事物的一种描述 类的组成 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸) 行为:指事物能执行的操作,例如:手机...

    文章目录

    1. 类和对象

    1.1 类和对象的理解

    客观存在的事物皆为对象 ,所以我们也常常说万物皆对象。

      • 类的理解
        • 类是对现实生活中一类具有共同属性和行为的事物的抽象
        • 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
        • 简单理解:类就是对现实事物的一种描述
      • 类的组成
        • 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
        • 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
    • 类和对象的关系
      • 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
      • 对象:是能够看得到摸的着的真实存在的实体
      • 简单理解:类是对事物的一种描述,对象则为具体存在的事物

    1.2 类的定义

    类的组成是由属性和行为两部分组成

    • 属性:在类中通过成员变量来体现(类中方法外的变量)
    • 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可)

    类的定义步骤:

    ①定义类

    ②编写类的成员变量

    ③编写类的成员方法

    public class 类名 {
    	// 成员变量
    	变量1的数据类型 变量1;
    	变量2的数据类型 变量2;// 成员方法
    	方法1;
    	方法2;	
    }
    

    示例代码:

    /*
        手机类:
            类名:
            手机(Phone)
    
            成员变量:
            品牌(brand)
            价格(price)
    
            成员方法:
            打电话(call)
            发短信(sendMessage)
     */
    public class Phone {
        //成员变量
        String brand;
        int price;
    
        //成员方法
        public void call() {
            System.out.println("打电话");
        }
    
        public void sendMessage() {
            System.out.println("发短信");
        }
    }
    
    

    1.3 对象的使用

    • 创建对象的格式:
      • 类名 对象名 = new 类名();
    • 调用成员的格式:
      • 对象名.成员变量
      • 对象名.成员方法();
    • 示例代码
    /*
        创建对象
            格式:类名 对象名 = new 类名();
            范例:Phone p = new Phone();
    
        使用对象
            1:使用成员变量
                格式:对象名.变量名
                范例:p.brand
            2:使用成员方法
                格式:对象名.方法名()
                范例:p.call()
     */
    public class PhoneDemo {
        public static void main(String[] args) {
            //创建对象
            Phone p = new Phone();
    
            //使用成员变量
            System.out.println(p.brand);
            System.out.println(p.price);
    
            p.brand = "小米";
            p.price = 2999;
    
            System.out.println(p.brand);
            System.out.println(p.price);
    
            //使用成员方法
            p.call();
            p.sendMessage();
        }
    }
    

    1.4 学生对象-练习

    • 需求:首先定义一个学生类,然后定义一个学生测试类,在学生测试类中通过对象完成成员变量和成员方法的使用
    • 分析:
      • 成员变量:姓名,年龄…
      • 成员方法:学习,做作业…
    • 示例代码:
    class Student {
        //成员变量
        String name;
        int age;
    
        //成员方法
        public void study() {
            System.out.println("好好学习,天天向上");
        }
    
        public void doHomework() {
            System.out.println("键盘敲烂,月薪过万");
        }
    }
    /*
        学生测试类
     */
    public class StudentDemo {
        public static void main(String[] args) {
            //创建对象
            Student s = new Student();
    
            //使用对象
            System.out.println(s.name + "," + s.age);
    
            s.name = "林青霞";
            s.age = 30;
    
            System.out.println(s.name + "," + s.age);
    
            s.study();
            s.doHomework();
        }
    }
    

    2. 对象内存图

    2.1 单个对象内存图

    • 成员变量使用过程

    在这里插入图片描述

    • 成员方法调用过程

    在这里插入图片描述

    2.2 多个对象内存图

    • 成员变量使用过程

    在这里插入图片描述

    • 成员方法调用过程

    在这里插入图片描述

    • 总结:

      多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份

    2.3 多个对象指向相同内存图

    在这里插入图片描述

    • 总结

      当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的)

      只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。

    3. 成员变量和局部变量

    3.1 成员变量和局部变量的区别

    • 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
    • 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
    • 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
    • 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)

    4. 封装

    4.1 private关键字

    private是一个修饰符,可以用来修饰成员(成员变量,成员方法)

    • 被private修饰的成员,只能在本类进行访问,针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作

      • 提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰
      • 提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰
    • 示例代码:

      /*
          学生类
       */
      class Student {
          //成员变量
          String name;
          private int age;
      
          //提供get/set方法
          public void setAge(int a) {
              if(a<0 || a>120) {
                  System.out.println("你给的年龄有误");
              } else {
                  age = a;
              }
          }
      
          public int getAge() {
              return age;
          }
      
          //成员方法
          public void show() {
              System.out.println(name + "," + age);
          }
      }
      /*
          学生测试类
       */
      public class StudentDemo {
          public static void main(String[] args) {
              //创建对象
              Student s = new Student();
              //给成员变量赋值
              s.name = "林青霞";
              s.setAge(30);
              //调用show方法
              s.show();
          }
      }
      

    4.2 private的使用

    • 需求:定义标准的学生类,要求name和age使用private修饰,并提供set和get方法以及便于显示数据的show方法,测试类中创建对象并使用,最终控制台输出 林青霞,30

    • 示例代码:

      /*
          学生类
       */
      class Student {
          //成员变量
          private String name;
          private int age;
      
          //get/set方法
          public void setName(String n) {
              name = n;
          }
      
          public String getName() {
              return name;
          }
      
          public void setAge(int a) {
              age = a;
          }
      
          public int getAge() {
              return age;
          }
      
          public void show() {
              System.out.println(name + "," + age);
          }
      }
      /*
          学生测试类
       */
      public class StudentDemo {
          public static void main(String[] args) {
              //创建对象
              Student s = new Student();
      
              //使用set方法给成员变量赋值
              s.setName("林青霞");
              s.setAge(30);
      
              s.show();
      
              //使用get方法获取成员变量的值
              System.out.println(s.getName() + "---" + s.getAge());
              System.out.println(s.getName() + "," + s.getAge());
      
          }
      }
      

    4.3 this关键字

    • this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
      • 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
      • 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
    public class Student {
        private String name;
        private int age;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public int getAge() {
            return age;
        }
    
        public void show() {
            System.out.println(name + "," + age);
        }
    }
    

    4.4 this内存原理

    • this代表当前调用方法的引用,哪个对象调用的方法,this就代表哪一个对象

    • 示例代码:

      public class StudentDemo {
          public static void main(String[] args) {
              Student s1 = new Student();
              s1.setName("林青霞");
              Student s2 = new Student();
              s2.setName("张曼玉");
          }
      }
      
    • 图解:

    在这里插入图片描述

    4.5 封装思想

    1. 封装概述
      是面向对象三大特征之一(封装,继承,多态)
      是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
    2. 封装原则
      将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
      成员变量private,提供对应的getXxx()/setXxx()方法
    3. 封装好处
      通过方法来控制成员变量的操作,提高了代码的安全性
      把代码用方法进行封装,提高了代码的复用性

    5. 构造方法

    5.1 构造方法概述

    构造方法是一种特殊的方法

    • 作用:创建对象 Student stu = new Student();

    • 格式:

      public class 类名{

      ​ 修饰符 类名( 参数 ) {

      ​ }

      }

    • 功能:主要是完成对象数据的初始化

    • 示例代码:

    class Student {
        private String name;
        private int age;
    
        //构造方法
        public Student() {
            System.out.println("无参构造方法");
        }
    
        public void show() {
            System.out.println(name + "," + age);
        }
    }
    /*
        测试类
     */
    public class StudentDemo {
        public static void main(String[] args) {
            //创建对象
            Student s = new Student();
            s.show();
        }
    }
    

    5.2 构造方法的注意事项

    • 构造方法的创建

    如果没有定义构造方法,系统将给出一个默认的无参数构造方法
    如果定义了构造方法,系统将不再提供默认的构造方法

    • 构造方法的重载

    如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法

    • 推荐的使用方式

    无论是否使用,都手工书写无参数构造方法

    • 重要功能!

    可以使用带参构造,为成员变量进行初始化

    • 示例代码
    /*
        学生类
     */
    class Student {
        private String name;
        private int age;
    
        public Student() {}
    
        public Student(String name) {
            this.name = name;
        }
    
        public Student(int age) {
            this.age = age;
        }
    
        public Student(String name,int age) {
            this.name = name;
            this.age = age;
        }
    
        public void show() {
            System.out.println(name + "," + age);
        }
    }
    /*
        测试类
     */
    public class StudentDemo {
        public static void main(String[] args) {
            //创建对象
            Student s1 = new Student();
            s1.show();
    
            //public Student(String name)
            Student s2 = new Student("林青霞");
            s2.show();
    
            //public Student(int age)
            Student s3 = new Student(30);
            s3.show();
    
            //public Student(String name,int age)
            Student s4 = new Student("林青霞",30);
            s4.show();
        }
    }
    

    5.3 标准类制作

    • 需求:定义标准学生类,要求分别使用空参和有参构造方法创建对象,空参创建的对象通过setXxx赋值,有参创建的对象直接赋值,并通过show方法展示数据。
    • 示例代码:
    class Student {
        //成员变量
        private String name;
        private int age;
    
        //构造方法
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        //成员方法
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public int getAge() {
            return age;
        }
    
        public void show() {
            System.out.println(name + "," + age);
        }
    }
    /*
        创建对象并为其成员变量赋值的两种方式
            1:无参构造方法创建对象后使用setXxx()赋值
            2:使用带参构造方法直接创建带有属性值的对象
    */
    public class StudentDemo {
        public static void main(String[] args) {
            //无参构造方法创建对象后使用setXxx()赋值
            Student s1 = new Student();
            s1.setName("林青霞");
            s1.setAge(30);
            s1.show();
    
            //使用带参构造方法直接创建带有属性值的对象
            Student s2 = new Student("林青霞",30);
            s2.show();
        }
    }
    

    6. 继承

    6.1 继承的实现

    • 继承的概念

      • 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
    • 实现继承的格式

      • 继承通过extends实现
      • 格式:class 子类 extends 父类 { }
        • 举例:class Dog extends Animal { }
    • 继承带来的好处

      • 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
    • 示例代码

      public class Fu {
          public void show() {
              System.out.println("show方法被调用");
          }
      }
      public class Zi extends Fu {
          public void method() {
              System.out.println("method方法被调用");
          }
      }
      public class Demo {
          public static void main(String[] args) {
              //创建对象,调用方法
              Fu f = new Fu();
              f.show();
      
              Zi z = new Zi();
              z.method();
              z.show();
          }
      }
      

    6.2 继承的好处和弊端

    • 继承好处
      • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
      • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
    • 继承弊端
      • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
    • 继承的应用场景:
      • 使用继承,需要考虑类与类之间是否存在is…a的关系,不能盲目使用继承
        • is…a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类

    7. 继承中的成员访问特点

    7.1 继承中变量的访问特点

    在子类方法中访问一个变量,采用的是就近原则。

    1. 子类局部范围找
    2. 子类成员范围找
    3. 父类成员范围找
    4. 如果都没有就报错(不考虑父亲的父亲…)
    • 示例代码

      class Fu {
          int num = 10;
      }
      class Zi {
          int num = 20;
          public void show(){
              int num = 30;
              System.out.println(num);
          }
      }
      public class Demo1 {
          public static void main(String[] args) {
              Zi z = new Zi();
              z.show();	// 输出show方法中的局部变量30
          }
      }
      

    7.2 super

    • this&super关键字:
      • this:代表本类对象的引用
      • super:代表父类存储空间的标识(可以理解为父类对象引用)
    • this和super的使用分别
      • 成员变量:
        • this.成员变量 - 访问本类成员变量
        • super.成员变量 - 访问父类成员变量
      • 成员方法:
        • this.成员方法 - 访问本类成员方法
        • super.成员方法 - 访问父类成员方法
    • 构造方法:
      • this(…) - 访问本类构造方法
      • super(…) - 访问父类构造方法

    7.3 继承中构造方法的访问特点

    注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

    ​ 子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

    问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

    1. 通过使用super关键字去显示的调用父类的带参构造方法
    2. 在父类中自己提供一个无参构造方法
    

    推荐方案:

    ​ 自己给出无参构造方法

    7.4 继承中成员方法的访问特点

    通过子类对象访问一个方法

    1. 子类成员范围找
    2. 父类成员范围找
    3. 如果都没有就报错(不考虑父亲的父亲…)

    7.5 super内存图

    • 对象在堆内存中,会单独存在一块super区域,用来存放父类的数据
      在这里插入图片描述

    7.6 方法重写

    • 1、方法重写概念
      • 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
    • 2、方法重写的应用场景
      • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
    • 3、Override注解
      • 用来检测当前的方法,是否是重写的方法,起到【校验】的作用

    7.7 方法重写的注意事项

    • 方法重写的注意事项
    1. 私有方法不能被重写(父类私有成员子类是不能继承的)
    2. 子类方法访问权限不能更低(public > 默认 > 私有)
    • 示例代码
    public class Fu {
        private void show() {
            System.out.println("Fu中show()方法被调用");
        }
    
        void method() {
            System.out.println("Fu中method()方法被调用");
        }
    }
    
    public class Zi extends Fu {
    
        /* 编译【出错】,子类不能重写父类私有的方法*/
        @Override
        private void show() {
            System.out.println("Zi中show()方法被调用");
        }
       
        /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
        @Override
        private void method() {
            System.out.println("Zi中method()方法被调用");
        }
    
        /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
        @Override
        public void method() {
            System.out.println("Zi中method()方法被调用");
        }
    }
    

    7.8. Java中继承的注意事项

    • Java中继承的注意事项

      1. Java中类只支持单继承,不支持多继承
        • 错误范例:class A extends B, C { }
      2. Java中类支持多层继承
    • 多层继承示例代码:

      public class Granddad {
      
          public void drink() {
              System.out.println("爷爷爱喝酒");
          }
      
      }
      
      public class Father extends Granddad {
      
          public void smoke() {
              System.out.println("爸爸爱抽烟");
          }
      
      }
      
      public class Mother {
      
          public void dance() {
              System.out.println("妈妈爱跳舞");
          }
      
      }
      public class Son extends Father {
      	// 此时,Son类中就同时拥有drink方法以及smoke方法
      }
      

    8. 继承练习

    8.1 老师和学生

    • 需求:定义老师类和学生类,然后写代码测试;最后找到老师类和学生类当中的共性内容,抽取出一个父类,用继承的方式改写代码,并进行测试

    • 步骤:

      ①定义老师类(姓名,年龄,教书())

      ②定义学生类(姓名,年龄,学习())

      ③定义测试类,写代码测试

      ④共性抽取父类,定义人类(姓名,年龄)

      ⑤定义老师类,继承人类,并给出自己特有方法:教书()

      ⑥定义学生类,继承人类,并给出自己特有方法:学习()

      ⑦定义测试类,写代码测试

    • 示例代码:

      class Person {
          private String name;
          private int age;
      
          public Person() {
          }
      
          public Person(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      }
      
      class Teacher extends Person {
      
          public Teacher() {}
      
          public Teacher(String name,int age) {
              super(name,age);
          }
      
          public void teach() {
              System.out.println("用爱成就每一位学员");
          }
      
      }
      
      class Student extends Person{
          public Student() {}
          
          public Student(String name, int age) {
              super(name,age);
          }
          
          public void study(){
              System.out.println("学生学习");
          }
          
      }
      
      class PersonDemo {
          public static void main(String[] args){
              //创建老师类对象并进行测试
              Teacher t1 = new Teacher();
              t1.setName("林青霞");
              t1.setAge(30);
              System.out.println(t1.getName() + "," + t1.getAge());
              t1.teach();
      
      
              Teacher t2 = new Teacher("风清扬", 33);
              System.out.println(t2.getName() + "," + t2.getAge());
              t2.teach();
              
              // 创建学生类对象测试
              Student s = new Student("张三"23)System.out.println(s.getName() + "," + s.getAge());
              s.study();
          }
      }
      

    8.2 猫和狗

    • 需求:请采用继承的思想实现猫和狗的案例,并在测试类中进行测试

    • 分析:

      ①猫:

      ​ 成员变量:姓名,年龄

      ​ 构造方法:无参,带参

      ​ 成员方法:get/set方法,抓老鼠()

      ②狗:

      ​ 成员变量:姓名,年龄

      ​ 构造方法:无参,带参

      ​ 成员方法:get/set方法,看门()

      ③共性:

      ​ 成员变量:姓名,年龄;构造方法:无参,带参;成员方法:get/set方法

    • 步骤:

      1、定义动物类(Animal)

      ​ 【成员变量:姓名,年龄】【 构造方法:无参,带参】【成员方法:get/set方法】

      2、定义猫类(Cat),继承动物类

      ​ 【构造方法:无参,带参】【成员方法:抓老鼠() 】

      3、定义狗类(Dog),继承动物类

      ​ 【构造方法:无参,带参】【成员方法:看门() 】

      4、定义测试类(AnimalDemo),写代码测试

    • 示例代码:

      class Animal {
          private String name;
          private int age;
      
          public Animal() {
          }
      
          public Animal(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      }
      class Cat extends Animal {
      
          public Cat() {
          }
      
          public Cat(String name, int age) {
              super(name, age);
          }
      
          public void catchMouse() {
              System.out.println("猫抓老鼠");
          }
      }
      class Dog extends Animal {
      
          public Dog() {
          }
      
          public Dog(String name, int age) {
              super(name, age);
          }
      
          public void lookDoor() {
              System.out.println("狗看门");
          }
      }
      /*
          测试类
       */
      public class AnimalDemo {
          public static void main(String[] args) {
              //创建猫类对象并进行测试
              Cat c1 = new Cat();
              c1.setName("加菲猫");
              c1.setAge(5);
              System.out.println(c1.getName() + "," + c1.getAge());
              c1.catchMouse();
      
              Cat c2 = new Cat("加菲猫", 5);
              System.out.println(c2.getName() + "," + c2.getAge());
              c2.catchMouse();
          }
      }
      

    9. 修饰符

    9.1 package

    • 1、包的概念
      • 包就是文件夹,用来管理类文件的
    • 2、包的定义格式
      • package 包名; (多级包用.分开)
      • 例如:package com.heima.demo;
    • 3、带包编译&带包运行
      • 带包编译:javac –d . 类名.java
        • 例如:javac -d . com.heima.demo.HelloWorld.java
      • 带包运行:java 包名+类名
        • 例如:java com.heima.demo.HelloWorld

    9.2 import

    • 导包的意义

      使用不同包下的类时,使用的时候要写类的全路径,写起来太麻烦了

      为了简化带包的操作,Java就提供了导包的功能

    • 导包的格式

      格式:import 包名;

      范例:import java.util.Scanner;

    • 示例代码(没有使用导包,创建的Scanner对象)

    package com.heima;
    
    public class Demo {
        public static void main(String[] args) {
            // 1. 没有导包,创建Scnaner对象
            java.util.Scanner sc = new java.util.Scanner(System.in);
        }
    }
    
    • 示例代码(使用导包后,创建的Scanner对象)
    package com.heima;
    
    import java.util.Scanner;
    
    public class Demo {
        public static void main(String[] args) {
            // 1. 没有导包,创建Scnaner对象
            Scanner sc = new Scanner(System.in);
        }
    }
    

    9.3 权限修饰符

    在这里插入图片描述

    9.4 final

    • fianl关键字的作用
      • final代表最终的意思,可以修饰成员方法,成员变量,类
    • final修饰类、方法、变量的效果
      • fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
      • final修饰方法:该方法不能被重写
      • final修饰变量:表明该变量是一个常量,不能再次赋值

    9.5 final修饰局部变量

    • fianl修饰基本数据类型变量

      • final 修饰指的是基本类型的数据值不能发生改变
    • final修饰引用数据类型变量

      • final 修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的

      • 举例:

        public static void main(String[] args){
            final Student s = new Student(23);
          	s = new Student(24);  // 错误
         	s.setAge(24);  // 正确
        }
        

    9.6 static

    • static的概念
      • static关键字是静态的意思,可以修饰【成员方法】,【成员变量】
    • static修饰的特点
      1. 被类的所有对象共享,这也是我们判断是否使用静态关键字的条件
      2. 可以通过类名调用当然,也可以通过对象名调用**【推荐使用类名调用】**
    • 示例代码:
    class Student {
    
        public String name; //姓名
        public int age; //年龄
        public static String university; //学校	共享数据!所以设计为静态!
    
        public void show() {
            System.out.println(name + "," + age + "," + university);
        }
    
    }
    
    public class StaticDemo {
        public static void main(String[] args) {
    	    // 为对象的共享数据赋值
            Student.university = "传智大学";
    
            Student s1 = new Student();
            s1.name = "林青霞";
            s1.age = 30;
            s1.show();
    
            Student s2 = new Student();
            s2.name = "风清扬";
            s2.age = 33;
            s2.show();
        }
    }
    

    9.7 static访问特点

    • static的访问特点
      • 非静态的成员方法
        • 能访问静态的成员变量
        • 能访问非静态的成员变量
        • 能访问静态的成员方法
        • 能访问非静态的成员方法
      • 静态的成员方法
        • 能访问静态的成员变量
        • 能访问静态的成员方法
      • 总结成一句话就是:
        • 静态成员方法只能访问静态成员

    10. 多态

    10.1 多态的概述

    • 什么是多态

      ​ 同一个对象,在不同时刻表现出来的不同形态

    • 多态的前提

      • 要有继承或实现关系
      • 要有方法的重写
      • 要有父类引用指向子类对象

    10.2 多态中的成员访问特点

    • 成员访问特点

      • 成员变量

        ​ 编译看父类,运行看父类

      • 成员方法

        ​ 编译看父类,运行看子类

    • 代码演示

      • 动物类

        public class Animal {
            public int age = 40;
        
            public void eat() {
                System.out.println("动物吃东西");
            }
        }
        
      • 猫类

        public class Cat extends Animal {
            public int age = 20;
            public int weight = 10;
        
            @Override
            public void eat() {
                System.out.println("猫吃鱼");
            }
        
            public void playGame() {
                System.out.println("猫捉迷藏");
            }
        }
        
      • 测试类

        public class AnimalDemo {
            public static void main(String[] args) {
                //有父类引用指向子类对象
                Animal a = new Cat();
        
                System.out.println(a.age);
        //        System.out.println(a.weight);
        
                a.eat();
        //        a.playGame();
            }
        }
        

    10.3 多态的好处和弊端

    • 好处

      ​ 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

    • 弊端

      ​ 不能使用子类的特有成员

    10.4 多态中的转型

    • 向上转型

      ​ 父类引用指向子类对象就是向上转型

    • 向下转型

      ​ 格式:子类型 对象名 = (子类型)父类引用;

    • 代码演示

      • 动物类
      public class Animal {
          public void eat() {
              System.out.println("动物吃东西");
          }
      }
      
      • 猫类
      public class Cat extends Animal {
          @Override
          public void eat() {
              System.out.println("猫吃鱼");
          }
      
          public void playGame() {
              System.out.println("猫捉迷藏");
          }
      }
      
      • 测试类
      public class AnimalDemo {
          public static void main(String[] args) {
              //多态
              //向上转型
              Animal a = new Cat();
              a.eat();
      //      a.playGame();
      
      
              //向下转型
              Cat c = (Cat)a;
              c.eat();
              c.playGame();
          }
      }
      

    10.5 多态的案例

    案例需求

    • 请采用多态的思想实现猫和狗的案例,并在测试类中进行测试

    代码实现

    • 动物类

      public class Animal {
          private String name;
          private int age;
      
          public Animal() {
          }
      
          public Animal(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public void eat() {
              System.out.println("动物吃东西");
          }
      }
      
      
    • 猫类

      public class Cat extends Animal {
      
          public Cat() {
          }
      
          public Cat(String name, int age) {
              super(name, age);
          }
      
          @Override
          public void eat() {
              System.out.println("猫吃鱼");
          }
      }
      
    • 狗类

      public class Dog extends Animal {
      
          public Dog() {
          }
      
          public Dog(String name, int age) {
              super(name, age);
          }
      
          @Override
          public void eat() {
              System.out.println("狗吃骨头");
          }
      }
      
      
    • 测试类

      public class AnimalDemo {
          public static void main(String[] args) {
              //创建猫类对象进行测试
              Animal a = new Cat();
              a.setName("加菲");
              a.setAge(5);
              System.out.println(a.getName() + "," + a.getAge());
              a.eat();
      
              a = new Cat("加菲", 5);
              System.out.println(a.getName() + "," + a.getAge());
              a.eat();
      
          }
      }
      
      

    11. 抽象类

    11.1 抽象类的概述

    ​ 当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!

    ​ 在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!

    11.2 抽象类的特点

    • 抽象类和抽象方法必须使用 abstract 关键字修饰

      //抽象类的定义
      public abstract class 类名 {}
      //抽象方法的定义
      public abstract void eat();
      
    • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

    • 抽象类不能实例化

      ​ 抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态

    • 抽象类的子类

      ​ 要么重写抽象类中的所有抽象方法

      ​ 要么是抽象类

    11.3 抽象类的成员特点

    • 成员的特点

      • 成员变量
        • 既可以是变量
        • 也可以是常量
      • 构造方法
        • 空参构造
        • 有参构造
      • 成员方法
        • 抽象方法
        • 普通方法
    • 代码演示

      • 动物类
      public abstract class Animal {
      
            private int age = 20;
            private final String city = "北京";
        
            public Animal() {}
        
            public Animal(int age) {
                this.age = age;
            }
        
        
            public void show() {
                age = 40;
                System.out.println(age);
                //        city = "上海";
                System.out.println(city);
            }
        
            public abstract void eat();
        }
      
      • 猫类
        public class Cat extends Animal {
      
            @Override
            public void eat() {
                System.out.println("猫吃鱼");
            }
        }
      
      • 测试类
        public class AnimalDemo {
            public static void main(String[] args) {
                Animal a = new Cat();
                a.eat();
                a.show();
            }
        }
      

    11.4 抽象类的案例

    • 案例需求

      ​ 请采用抽象类的思想实现猫和狗的案例,并在测试类中进行测试

    • 代码实现

      • 动物类
        public abstract class Animal {
            private String name;
            private int age;
        
            public Animal() {
            }
        
            public Animal(String name, int age) {
                this.name = name;
                this.age = age;
            }
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public int getAge() {
                return age;
            }
        
            public void setAge(int age) {
                this.age = age;
            }
        
            public abstract void eat();
        }
      
      • 猫类
        public class Cat extends Animal {
        
            public Cat() {
            }
        
            public Cat(String name, int age) {
                super(name, age);
            }
        
            @Override
            public void eat() {
                System.out.println("猫吃鱼");
            }
        }
      
      • 狗类
        public class Dog extends Animal {
        
            public Dog() {
            }
        
            public Dog(String name, int age) {
                super(name, age);
            }
        
            @Override
            public void eat() {
                System.out.println("狗吃骨头");
            }
        }
      
      
      • 测试类
        public class AnimalDemo {
            public static void main(String[] args) {
                //创建对象,按照多态的方式
                Animal a = new Cat();
                a.setName("加菲");
                a.setAge(5);
                System.out.println(a.getName()+","+a.getAge());
                a.eat();
                System.out.println("--------");
        
                a = new Cat("加菲",5);
                System.out.println(a.getName()+","+a.getAge());
                a.eat();
            }
        }
      

    12. 接口

    12.1 接口的概述

    ​ 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。

    ​ Java中的接口更多的体现在对行为的抽象!

    12.2 接口的特点

    • 接口用关键字interface修饰

      public interface 接口名 {} 
      
    • 类实现接口用implements表示

      public class 类名 implements 接口名 {}
      
    • 接口不能实例化

      ​ 接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态。

      ​ 多态的形式:具体类多态,抽象类多态,接口多态。

    • 接口的子类

      ​ 要么重写接口中的所有抽象方法

      ​ 要么子类也是抽象类

    12.3 接口的成员特点

    • 成员特点

      • 成员变量

        ​ 只能是常量
        ​ 默认修饰符:public static final

      • 构造方法

        ​ 没有,因为接口主要是扩展功能的,而没有具体存在

      • 成员方法

        ​ 只能是抽象方法

        ​ 默认修饰符:public abstract

        ​ 关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解

    • 代码演示

      • 接口
        public interface Inter {
            public int num = 10;
            public final int num2 = 20;
        //    public static final int num3 = 30;
            int num3 = 30;
        
        //    public Inter() {}
        
        //    public void show() {}
        
            public abstract void method();
            void show();
        
        }
      
      • 实现类
        public class InterImpl extends Object implements Inter {
            public InterImpl() {
                super();
            }
        
            @Override
            public void method() {
                System.out.println("method");
            }
        
            @Override
            public void show() {
                System.out.println("show");
            }
        }
      
      • 测试类
        public class InterfaceDemo {
            public static void main(String[] args) {
                Inter i = new InterImpl();
        //        i.num = 20;
                System.out.println(i.num);
        //        i.num2 = 40;
                System.out.println(i.num2);
                System.out.println(Inter.num);
            }
        }
      

    12.4 接口的案例

    • 案例需求

      ​ 对猫和狗进行训练,他们就可以跳高了,这里加入跳高功能。

      ​ 请采用抽象类和接口来实现猫狗案例,并在测试类中进行测试。

    • 代码实现

      • 动物类
        public abstract class Animal {
            private String name;
            private int age;
        
            public Animal() {
            }
        
            public Animal(String name, int age) {
                this.name = name;
                this.age = age;
            }
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public int getAge() {
                return age;
            }
        
            public void setAge(int age) {
                this.age = age;
            }
        
            public abstract void eat();
        }
      
      
      • 跳高接口
        public interface Jumpping {
            public abstract void jump();
        }
      
      • 猫类
        public class Cat extends Animal implements Jumpping {
        
            public Cat() {
            }
        
            public Cat(String name, int age) {
                super(name, age);
            }
        
            @Override
            public void eat() {
                System.out.println("猫吃鱼");
            }
        
            @Override
            public void jump() {
                System.out.println("猫可以跳高了");
            }
        }
      
      
      • 测试类
        public class AnimalDemo {
            public static void main(String[] args) {
                //创建对象,调用方法
                Jumpping j = new Cat();
                j.jump();
                System.out.println("--------");
        
                Animal a = new Cat();
                a.setName("加菲");
                a.setAge(5);
                System.out.println(a.getName()+","+a.getAge());
                a.eat();
        //        a.jump();
        
                a = new Cat("加菲",5);
                System.out.println(a.getName()+","+a.getAge());
                a.eat();
                System.out.println("--------");
        
                Cat c = new Cat();
                c.setName("加菲");
                c.setAge(5);
                System.out.println(c.getName()+","+c.getAge());
                c.eat();
                c.jump();
            }
        }
      

    12.5 类和接口的关系

    • 类与类的关系

      ​ 继承关系,只能单继承,但是可以多层继承

    • 类与接口的关系

      ​ 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

    • 接口与接口的关系

      ​ 继承关系,可以单继承,也可以多继承

    12.6 抽象类和接口的区别

    • 成员区别

      • 抽象类

        ​ 变量,常量;有构造方法;有抽象方法,也有非抽象方法

      • 接口

        ​ 常量;抽象方法

    • 关系区别

      • 类与类

        ​ 继承,单继承

      • 类与接口

        ​ 实现,可以单实现,也可以多实现

      • 接口与接口

        ​ 继承,单继承,多继承

    • 设计理念区别

      • 抽象类

        ​ 对类抽象,包括属性、行为

      • 接口

        ​ 对行为抽象,主要是行为

    13. 综合案例

    13.1 案例需求

    ​ 我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。

    ​ 为了出国交流,跟乒乓球相关的人员都需要学习英语。

    ​ 请用所学知识分析,这个案例中有哪些具体类,哪些抽象类,哪些接口,并用代码实现。

    在这里插入图片描述

    13.2 代码实现

    • 抽象人类
    public abstract class Person {
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public abstract void eat();
    }
    
    • 抽象运动员类
    public abstract class Player extends Person {
        public Player() {
        }
    
        public Player(String name, int age) {
            super(name, age);
        }
    
        public abstract void study();
    }
    
    
    • 抽象教练类
    public abstract class Coach extends Person {
        public Coach() {
        }
    
        public Coach(String name, int age) {
            super(name, age);
        }
    
        public abstract void teach();
    }
    
    • 学英语接口
    public interface SpeakEnglish {
        public abstract void speak();
    }
    
    • 蓝球教练
    public class BasketballCoach extends Coach {
        public BasketballCoach() {
        }
    
        public BasketballCoach(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void teach() {
            System.out.println("篮球教练教如何运球和投篮");
        }
    
        @Override
        public void eat() {
            System.out.println("篮球教练吃羊肉,喝羊奶");
        }
    }
    
    • 乒乓球教练
    public class PingPangCoach extends Coach implements SpeakEnglish {
    
        public PingPangCoach() {
        }
    
        public PingPangCoach(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void teach() {
            System.out.println("乒乓球教练教如何发球和接球");
        }
    
        @Override
        public void eat() {
            System.out.println("乒乓球教练吃小白菜,喝大米粥");
        }
    
        @Override
        public void speak() {
            System.out.println("乒乓球教练说英语");
        }
    }
    
    
    • 乒乓球运动员
    public class PingPangPlayer extends Player implements SpeakEnglish {
    
        public PingPangPlayer() {
        }
    
        public PingPangPlayer(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void study() {
            System.out.println("乒乓球运动员学习如何发球和接球");
        }
    
        @Override
        public void eat() {
            System.out.println("乒乓球运动员吃大白菜,喝小米粥");
        }
    
        @Override
        public void speak() {
            System.out.println("乒乓球运动员说英语");
        }
    }
    
    
    • 篮球运动员
    public class BasketballPlayer extends Player {
    
        public BasketballPlayer() {
        }
    
        public BasketballPlayer(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void study() {
            System.out.println("篮球运动员学习如何运球和投篮");
        }
    
        @Override
        public void eat() {
            System.out.println("篮球运动员吃牛肉,喝牛奶");
        }
    }
    
    

    14. 参数传递

    14.1 类名作为形参和返回值

    • 1、类名作为方法的形参

      方法的形参是类名,其实需要的是该类的对象

      实际传递的是该对象的【地址值】

    • 2、类名作为方法的返回值

      方法的返回值是类名,其实返回的是该类的对象

      实际传递的,也是该对象的【地址值】

    • 示例代码:

      class Cat {
          public void eat() {
              System.out.println("猫吃鱼");
          }
      }
      class CatOperator {
          public void useCat(Cat c) { //Cat c = new Cat();
              c.eat();
          }
          public Cat getCat() {
              Cat c = new Cat();
              return c;
          }
      }
      public class CatDemo {
          public static void main(String[] args) {
              //创建操作类对象,并调用方法
              CatOperator co = new CatOperator();
              Cat c = new Cat();
              co.useCat(c);
      
              Cat c2 = co.getCat(); //new Cat()
              c2.eat();
          }
      }
      

    14.2 抽象类作为形参和返回值

    • 抽象类作为形参和返回值

      • 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
      • 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
    • 示例代码:

      abstract class Animal {
          public abstract void eat();
      }
      class Cat extends Animal {
          @Override
          public void eat() {
              System.out.println("猫吃鱼");
          }
      }
      class AnimalOperator {
          public void useAnimal(Animal a) { //Animal a = new Cat();
              a.eat();
          }
          public Animal getAnimal() {
              Animal a = new Cat();
              return a;
          }
      }
      public class AnimalDemo {
          public static void main(String[] args) {
              //创建操作类对象,并调用方法
              AnimalOperator ao = new AnimalOperator();
              Animal a = new Cat();
              ao.useAnimal(a);
      
              Animal a2 = ao.getAnimal(); //new Cat()
              a2.eat();
          }
      }
      

    14.3 接口名作为形参和返回值

    • 接口作为形参和返回值

      • 方法的形参是接口名,其实需要的是该接口的实现类对象
      • 方法的返回值是接口名,其实返回的是该接口的实现类对象
    • 示例代码:

      interface Jumpping {
          void jump();
      }
      class JumppingOperator {
          public void useJumpping(Jumpping j) { //Jumpping j = new Cat();
              j.jump();
          }
          public Jumpping getJumpping() {
              Jumpping j = new Cat();
              return j;
          }
      }
      class Cat implements Jumpping {
          @Override
          public void jump() {
              System.out.println("猫可以跳高了");
          }
      }
      public class JumppingDemo {
          public static void main(String[] args) {
              //创建操作类对象,并调用方法
              JumppingOperator jo = new JumppingOperator();
              Jumpping j = new Cat();
              jo.useJumpping(j);
      
              Jumpping j2 = jo.getJumpping(); //new Cat()
              j2.jump();
          }
      }
      
      

    15. 内部类

    15.1 内部类的基本使用

    • 内部类概念

      • 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
    • 内部类定义格式

      • 格式&举例:

        /*
        	格式:
            class 外部类名{
            	修饰符 class 内部类名{
            	
            	}
            }
        */
        
        class Outer {
            public class Inner {
                
            }
        }
        
    • 内部类的访问特点

      • 内部类可以直接访问外部类的成员,包括私有
      • 外部类要访问内部类的成员,必须创建对象
    • 示例代码:

      /*
          内部类访问特点:
              内部类可以直接访问外部类的成员,包括私有
              外部类要访问内部类的成员,必须创建对象
       */
      public class Outer {
          private int num = 10;
          public class Inner {
              public void show() {
                  System.out.println(num);
              }
          }
          public void method() {
              Inner i = new Inner();
              i.show();
          }
      }
      

    15.2 成员内部类

    • 成员内部类的定义位置

      • 在类中方法,跟成员变量是一个位置
    • 外界创建成员内部类格式

      • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
      • 举例:Outer.Inner oi = new Outer().new Inner();
    • 成员内部类的推荐使用方案

      • 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
    • 示例代码:

      class Outer {
          private int num = 10;
          private class Inner {
              public void show() {
                  System.out.println(num);
              }
          }
          public void method() {
              Inner i = new Inner();
              i.show();
          }
      }
      public class InnerDemo {
          public static void main(String[] args) {
      		//Outer.Inner oi = new Outer().new Inner();
      		//oi.show();
              Outer o = new Outer();
              o.method();
          }
      }
      

    15.3 局部内部类

    • 局部内部类定义位置

      • 局部内部类是在方法中定义的类
    • 局部内部类方式方式

      • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
      • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
    • 示例代码

      class Outer {
          private int num = 10;
          public void method() {
              int num2 = 20;
              class Inner {
                  public void show() {
                      System.out.println(num);
                      System.out.println(num2);
                  }
              }
              Inner i = new Inner();
              i.show();
          }
      }
      public class OuterDemo {
          public static void main(String[] args) {
              Outer o = new Outer();
              o.method();
          }
      }
      
      

    15.4 匿名内部类

    • 匿名内部类的前提

      • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
    • 匿名内部类的格式

      • 格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }

      • 举例:

        new Inter(){
            @Override
            public void method(){}
        } 
        
    • 匿名内部类的本质

      • 本质:是一个继承了该类或者实现了该接口的子类匿名对象
    • 匿名内部类的细节

      • 匿名内部类可以通过多态的形式接受

        Inter i = new Inter(){
        	@Override
        	public void method(){
        	}
        }
        
    • 匿名内部类直接调用方法

      interface Inter{
      	void method();
      }
      class Test{
      	public static void main(String[] args){
      		new Inter(){
      			@Override
      			public void method(){
      				System.out.println("我是匿名内部类"); 
      			}
      		}.method();	// 直接调用方法
      	}
      }
      

    15.5 匿名内部类在开发中的使用

    • 匿名内部类在开发中的使用

      • 当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
    • 示例代码:

        public class JumppingDemo {
            public static void main(String[] args) {
                //需求:创建接口操作类的对象,调用method方法
                JumppingOperator jo = new JumppingOperator();
                Jumpping j = new Cat();
                jo.method(j);
        
                Jumpping j2 = new Dog();
                jo.method(j2);
                System.out.println("--------");
        
                jo.method(new Jumpping() {
                    @Override
                    public void jump() {
                        System.out.println("猫可以跳高了");
                    }
                });
        
                jo.method(new Jumpping() {
                    @Override
                    public void jump() {
                        System.out.println("狗可以跳高了");
                    }
                });
        
            }
        }
      
    展开全文
  • Java面向对象 - 封装、继承和多态的综合练习

    千次阅读 多人点赞 2021-01-16 10:54:10
    2.封装;3.继承;4.构造函数;5.super()和this()。 面向对象思想 构造器:new就是一个构造器,作用是:①分配空间;②赋初始值(避免错误,简化输入); new Object(Parameters)构造器调用构造函数,传参为了赋初始...
  • java 简述类的封装性、继承性、多态性

    千次阅读 热门讨论 2016-09-22 15:53:12
     封装性:通俗说就是一个盒子,多个对象、功能、组件等装在一个盒子里,内部具体是什么不知道,用到它时,使用特定方法或功能去调用它。即声明一个变量(其属性值是private)不能给其变量直接赋值,但可以通过方法...
  • 面向对象是一种思想,是基于面向过程而言的,面向对象是将功能等通过对象来实现,将功能封装对象之中,让对象实现具体的细节。这种思想是将数据作为第一位,而方法(算法)作为其次,是对数据的一种优化,操作起来...
  • JAVA面向对象四大特性:多态、继承、抽象、封装

    千次阅读 多人点赞 2018-04-20 15:40:06
    一个接口,多种实现)面向对象的四大特性:封装、继承、多态、抽象。从一定角度来看,封装和继承几乎都是为多态而准备的。是最重要的知识点。多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据...
  • java面向对象三大特性

    千次阅读 2021-03-01 19:31:10
    一、面向对象的概念 面向对象是一种符合人类思维习惯的编程思想。现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系。在程序中使用对象来映射现实中的事物使用对象的关系来描述事物之间的联系,...
  • 面向对象编程之继承、多态、封装、抽象类、接口、包继承类的继承格式为什么要继承继承的特点继承的种类及关键字访问权限多态向上转型动态绑定方法重写重写和重载的区别比较多态的优点封装实现Java的封装封装的优点...
  • 1.面向对象编程思想(封装继承多态接口)

    千次阅读 多人点赞 2016-11-28 09:22:52
    2.封装的目的是:增强安全和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。 3.封装的基本要求是:把所有的属性私有化,对每个属性提供getter和setter方法,...
  • 面向对象编程三大特性------封装、继承、多态

    万次阅读 多人点赞 2016-06-07 11:41:12
    本文是对面向对象编程三大特性(封装、继承、多态)的一个学习总结。 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的...
  • 2.封装的目的是:增强安全和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。 3.封装的基本要求是:把所有的属性私有化,对每个属性提供getter和setter方法,如果...
  • ​ 方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集 注意: 方法必须先创建才可以使用,该过程成为方法定义 方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程...
  • Java面向对象知识点前言一、封装二、多态三、继承四、抽象五级标题六级标题总结 前言 面向对象(Object Oriented)是软件开发方法,一种编程范式。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到...
  • 文章目录第1关:通关任务一任务描述相关知识面向对象思想封装继承`super()`和`this()`编程要求测试说明参考答案第2关:通关任务二任务描述相关知识重写和重载`abstract`(抽象类)和`interface`(接口)`final`关键字`...
  • java面向对象

    万次阅读 多人点赞 2018-08-21 16:51:59
    包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。 1、面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是...
  • 要想访问被封装对象中的数据,只能使用对象专门提供的对外接口,这个接口一般为方法。调用该方法能够获取对象内部数据。 在JavaScript语言中没有提供专门的信息封装关键字,不过可以使用闭包来创建,只允许从对象...
  • java 面向对象三大特性(封装,继承,多态)以及抽象、接口的介绍,this,super,构造方法的使用介绍
  • 封装 继承和多态

    千次阅读 2021-01-28 23:05:36
    封装继承和多态 封装 我们在设计程序时,讲究六个字,高内聚,低耦合 封装就是我们对数据进行隐藏,多用于来封装属性,封装方法较少。 记住封装的核心,就是私有(private) 令属性为私有后我们调用不了类里面的属性...
  • 本关任务:构造一个类,把对象的属性封装起来,同时提供一些可以被外界访问属性的方法。 相关知识 为了完成本关任务,你需要掌握:1.什么是封装;2.封装的意义;3.实现Java封装的步骤。 什么是封装 封装:就是隐藏...
  • 接触过面向对象的人都知道面向对象有三大特征,分别是封装、继承和多态。这三者分别指的是什么,为什么是这哥仨,使用他们有什么好处,我们来梳理一下。 封装  原则:隐藏对象的属性和实现细节,仅对外提供公共访问...
  • 类与对象实验一三角形、梯形和圆形的类封装实验目的: 使用类来封装对象的属性和功能实验要求:编写一个Java应用程序,该程序中有3个类:Trangle、Lader和Circle,分别用来刻画“三角形”“梯形”和“圆形”。...
  • 面向对象的程序设计读书笔记

    千次阅读 2017-10-17 23:33:02
    封装对象有一个明确的边界,声明公开,实现隐藏,实现的更改,对对象的使用没有影响。多态,子类的对象可以放到父类的引用中,子类的方法可以覆盖父类的公有非final、或者protected方法。方法可以同名重载。每个...
  • 1. 条件运算符(三元表达式) ,其形式为:  type d = a ? b : c; 具体化形式为:int d =2 2. switch(变量) //此处的变量类型就目前所学内容来看,只能为 4 种类型:byte,short, int, char { case 常量 1: /
  • 对象具有的各种特征:属性。 对象执行的操作:方法。 对象的属性和方法通常被封装在一起,共同体现事物的特性, 二者相辅相承,不能分割。方法的命名规则: 必须以字母、‘_’或‘$’开头。 可以包括数字,...
  • C#三大基本特性封装、继承、多态、封装
  • 面向对象思想精华总结

    千次阅读 多人点赞 2020-04-28 23:11:55
    封装 继承 多态 二、类图 泛化关系 (Generalization) 实现关系 (Realization) 聚合关系 (Aggregation) 组合关系 (Composition) 关联关系 (Association) 依赖关系 (Dependency) 三、设计原则 S.O.L.I.D 其他...
  • 面向对象的四大特性:封装、继承、多态、抽象。从一定角度来看,封装和继承几乎都是为多态而准备的。是最重要的知识点。多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用...
  • Java面向对象编程——多态的实现

    千次阅读 2017-12-18 20:41:13
    Java中多态的实现 一、使用父类类型的引用指向子类的对象 二、该引用只能调用父类中定义的方法和变量; 三、如果子类中重写了父类中的一个方法,那么在调用这个方法的 时候,将会调用子类中的这个方法;(动态...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 132,755
精华内容 53,102
关键字:

对象具有封装性的条件