java 面向对象_java面向对象 - CSDN
精华内容
参与话题
  • Java面向对象基础

    千次阅读 多人点赞 2018-05-29 17:38:11
    面向对象 学习内容 l 面向对象思想 l 类与对象及其使用 l 对象的内存图 l 成员变量和局部变量的区别 l 匿名对象 l 封装(private) l this关键字 l 构造方法 l ...

    面向对象

    学习内容

    1. 面向对象思想

    2. 类与对象及其使用

    3. 对象的内存图

    4. 成员变量和局部变量的区别

    5. 匿名对象

    6. 封装(private)

    7. this关键字

    8. 构造方法

    9. static关键字

    10. l 继承

    11. l 多态

    12. l 抽象类

    13. l 接口

    14. l 包和导包

    15. l 权限修饰符

    16. l 内部类


    一、面向对象思想

    l  前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式。

     

    面向过程思想概述

    l   我们来回想一下,这几天我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。

    l   在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。

    l   那么什么是面向过程开发呢?面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。

    l   面向过程的代表语言:C语言

     

    面向对象思想概述

    当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始思索,能不能把这些步骤和功能在进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。接下来我们看看面向对象到底是什么?

    面向对象思想概述

    •    面向对象是基于面向过程的编程思想

    面向对象思想特点

    •    是一种更符合我们思想习惯的思想

    •    可以将复杂的事情简单化

    •    将我们从执行者变成了指挥者

        •   角色发生了转换

     

    面向对象思想举例

    面向对象思想举例

    •    买电脑

    •    吃饭

    •    洗衣服

    •    万事万物皆对象

    面向过程思想和面向对象思想的代码举例

    •    把大象装进冰箱

     

    面向对象开发,设计,特征

    面向对象开发

    •    就是不断的创建对象,使用对象,指挥对象做事情。

    面向对象设计

    •    其实就是在管理和维护对象之间的关系。

    面向对象特征

    •    封装(encapsulation)

    •    继承(inheritance)

    •    多态(polymorphism)


    二、类与对象关系

    我们学习编程语言,就是为了模拟现实世界的事物,实现信息化。比如:去超市买东西的计费系统,去银行办业务的系统。

    我们如何表示一个现实世界事物呢:

    •    属性   就是该事物的描述信息

    •    行为   就是该事物能够做什么

    •    举例:学生事物

    我们学习的Java语言最基本单位是类,所以,我们就应该把事物用一个类来体现。

     

    类与对象关系

    类:是一组相关的属性和行为的集合

    对象:是该类事物的具体体现

    举例:

    •      学生

    •    对象   班长就是一个对象

     

    类与对象(图例)

    类与对象的关系如图

     

     

    类的定义

    现实世界的事物

    •    属性   人的身高,体重等

    •    行为   人可以学习,吃饭等

    Java中用class描述事物也是如此

    •    成员变量就是事物的属性

    •    成员方法就是事物的行为

    定义类其实就是定义类的成员(成员变量和成员方法)

     

    类与对象案例

    学生类

    •    如何定义

    •   按照事物到类的过程一步步分析

    •    如何使用

    •   创建对象:

    •   类名  对象名= new 类名();

    •   对象名.成员变量

    •   对象名.成员方法


    三、对象内存图

    1个对象的内存图

    •    一个对象的基本初始化过程

    2个对象的内存图

    •    方法的共用

    3个对象的内存图

    •    其中有两个引用指向同一个对象


    四、成员变量和局部变量的区别

    l  在类中的位置不同

    •     成员变量:类中方法外

    •     局部变量:方法内或者方法声明上

    l  在内存中的位置不同

    •     成员变量:堆内存

    •     局部变量:栈内存

    l  生命周期不同

    •     成员变量随着对象的存在而存在,随着对象的消失而消失

    •     局部变量随着方法的调用而存在,随着方法的调用完毕而消失

    l  初始化值不同

    •     成员变量有默认的初始化值

    •     局部变量没有默认的初始化值,必须先定义,赋值,才能使用。

     

    形式参数问题

    基本类型作为形式参数

    引用类型作为形式参数

    • 类作为形式参数的问题

    • 如果你看到一个方法需要的参数是一个类名,就应该知道这里实际需要的是一个具体的对象。

    /*
    	形式参数的问题:
    		基本类型:形式参数的改变不影响实际参数
    		引用类型:形式参数的改变直接影响实际参数
    */
    //形式参数是基本类型
    class Demo {
    	public int sum(int a,int b) {
    		return a + b;
    	}
    }
    
    //形式参数是引用类型
    class Student {
    	public void show() {
    		System.out.println("我爱学习");
    	}
    }
    
    class StudentDemo {
    	//如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象。
    	public void method(Student s) { //调用的时候,把main方法中的s的地址传递到了这里 Student s = new Student();
    		s.show();
    	}
    }
    
    class ArgsTest {
    	public static void main(String[] args) {
    		//形式参数是基本类型的调用
    		Demo d = new Demo();
    		int result = d.sum(10,20);
    		System.out.println("result:"+result);
    		System.out.println("--------------");
    		
    		//形式参数是引用类型的调用
    		//需求:我要调用StudentDemo类中的method()方法
    		StudentDemo sd = new StudentDemo();
    		//创建学生对象
    		Student s = new Student();
    		sd.method(s); //把s的地址给到了这里
    	}
    }

    五、匿名对象

    匿名对象:就是没有名字的对象。

    •    是对象的一种简化表示形式

    匿名对象的两种使用情况

    •    对象调用方法仅仅一次的时候

    •    作为实际参数传递

    /*
    	匿名对象:就是没有名字的对象。
    	
    	匿名对象的应用场景:
    		A:调用方法,仅仅只调用一次的时候。
    			注意:调用多次的时候,不适合。
    			那么,这种匿名调用有什么好处吗?
    				有,匿名对象调用完毕就是垃圾。可以被垃圾回收器回收。
    		B:匿名对象可以作为实际参数传递
    */
    class Student {
    	public void show() {
    		System.out.println("我爱学习");
    	}
    }
    
    class StudentDemo {
    	public void method(Student s) {
    		s.show();
    	}
    }
    
    class NoNameDemo {
    	public static void main(String[] args) {
    		//带名字的调用
    		Student s = new Student();
    		s.show();
    		s.show();
    		System.out.println("--------------");
    		
    		//匿名对象
    		//new Student();
    		//匿名对象调用方法
    		new Student().show();
    		new Student().show(); //这里其实是重新创建了一个新的对象
    		System.out.println("--------------");
    		
    		
    		//匿名对象作为实际参数传递
    		StudentDemo sd = new StudentDemo();
    		//Student ss = new Student();
    		//sd.method(ss); //这里的ss是一个实际参数
    		//匿名对象
    		sd.method(new Student());
    		
    		//在来一个
    		new StudentDemo().method(new Student());
     	}
    }

    六、封装

    封装概述

    •    是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

    好处:

    •     隐藏实现细节,提供公共的访问方式

    •     提高了代码的复用性

    •     提高安全性。

    封装原则:

    •     将不需要对外提供的内容都隐藏起来。

    •     把属性隐藏,提供公共方法对其访问。

    /*
    	定义一个学生类:
    		成员变量:name,age
    		成员方法:show()方法
    		
    	我们在使用这个案例的过程中,发现了一个问题:
    		通过对象去给成员变量赋值,可以赋值一些非法的数据。
    		这是不合理的。
    		应该是这个样子的:在赋值之前,先对数据进行判断。
    		判断到底在哪里做比较合适呢?
    		StudentDemo类是一个测试类,测试类一般只创建对象,调用方法。	
    		所以,这个判断应该定义在Student类中。
    		而我们在成员变量的位置可不可以进行数据判断呢?
    		是不可以的,因为做数据校验,必须要依靠一些逻辑语句。
    		逻辑语句是应该定义在方法中的,所以,我们最终决定在Student类中提供一个方法
    		来对数据进行校验。
    	
    	按照我们前面的分析,我们给出了一个方法进行校验。
    	但是呢,它偏偏不调用方法来赋值,还是直接赋值了,
    	这样我们的方法就没有起到作用。
    	我就应该要求你必须使用我的方法,而不能直接调用成员变量赋值。
    	怎么去强制要求不能直接使用成员变量呢?
    		针对这种情况,Java就提供了一个关键字 private
    		
    	private:私有的。可以修饰成员变量和成员方法。
    		注意:被private修饰的成员只能在本类中访问。
    		
    	其实我讲到现在讲解的是一个封装的思想。
    	封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
    */
    class Student {
    	//姓名
    	String name;
    	//年龄
    	private int age;
    	
    	//写一个方法对数据进行校验
    	/*
    		返回值类型:void
    		参数列表:int a
    	*/
    	public void setAge(int a) {
    		if(a < 0 || age > 120) {
    			System.out.println("你给的年龄有问题");
    		}else {
    			age = a;
    		}
    	}
    	
    	
    	//show()方法,显示所有成员变量值
    	public void show() {
    		System.out.println("姓名:"+name);
    		System.out.println("年龄:"+age);
    	}
    }
    
    class StudentDemo {
    	public static void main(String[] args) {
    		//创建学生对象
    		Student s = new Student();
    		s.show();
    		System.out.println("--------------");
    		
    		//给成员变量赋值
    		s.name = "林青霞";
    		//s.age = 27;
    		s.setAge(27);
    		s.show();
    		System.out.println("--------------");
    		
    		//给age赋值
    		//s.age = -27; //这个数据是不合理的
    		//通过方法给值
    		s.setAge(-27);
    		s.show();
    		System.out.println("--------------");
    	}
    }

     

    private关键字

    private关键字:

    •     是一个权限修饰符。

    •    可以修饰成员(成员变量和成员方法)

    •    private修饰的成员只在本类中才能访问。

    private最常见的应用:

    •    把成员变量用private修饰

    •    提供对应的getXxx()/setXxx()方法

    •     一个标准的案例的使用

    /*
    	封装和private的应用:
    		A:把成员变量用private修饰
    		B:提高对应的getXxx()和setXxx()方法
    */
    //定义学生类
    class Student {
    	//姓名
    	private String name;
    	//年龄
    	private int age;
    	
    	//姓名获取值
    	public String getName() {
    		return name;
    	}
    	
    	//姓名设置值
    	public void setName(String n) {
    		name = n;
    	}
    	
    	//年龄获取值
    	public int getAge() {
    		return age;
    	}
    	
    	//年龄赋值
    	public void setAge(int a) {
    		age = a;
    	}
    }
    
    //测试类
    class StudentTest {
    	public static void main(String[] args) {
    		//创建学生对象
    		Student s = new Student();
    		
    		//使用成员变量
    		//错误:被私有修饰了,外界不能直接访问了
    		//System.out.println(s.name+"---"+s.age);
    		System.out.println(s.getName()+"---"+s.getAge());
    		
    		//给成员变量赋值
    		//s.name = "林青霞";
    		//s.age = 27;
    		//通过方法给赋值
    		s.setName("林青霞");
    		s.setAge(27);
    		System.out.println(s.getName()+"---"+s.getAge());
    	}
    }

    七、this关键字

    this:代表所在类的对象引用

    记住:

    •    方法被哪个对象调用,this就代表那个对象

    什么时候使用this?

    •    局部变量隐藏成员变量

    •    其他用法后面和super一起讲解

    /*
    	标准的代码改进版
    	
    	this:哪个对象调用那个方法,this就代表那个对象
    */
    class Student {
    	private String name;
    	private int age;
    	
    	public String getName() {
    		return name; //这里其实是隐含了this
    	}
    	
    	public void setName(String name) {
    		this.name = name;
    	}
    	
    	public int getAge() {
    		return age;
    	}
    	
    	public void setAge(int age) {
    		this.age = age;
    	}
    }
    
    class StudentTest2 {
    	public static void main(String[] args) {
    		//创建一个对象
    		Student s1 = new Student();
    		s1.setName("林青霞");
    		s1.setAge(27);
    		System.out.println(s1.getName()+"---"+s1.getAge());
    		
    		//创建第二个对象
    		Student s2 = new Student();
    		s2.setName("刘意");
    		s2.setAge(30);
    		System.out.println(s2.getName()+"---"+s2.getAge());
    	}
    }

    八、构造方法

    构造方法作用概述

    •    给对象的数据进行初始化

    构造方法格式

    •    方法名与类名相同

    •    没有返回值类型,连void都没有

    •    没有具体的返回值

    | 思考题

    • 构造方法中可不可以有return语句呢?

    • 可以。而是我们写成这个样子就OK了:return;

    • 其实,在任何的void类型的方法的最后你都可以写上:return;

    构造方法注意事项

    •    如果你不提供构造方法,系统会给出默认构造方法

    •    如果你提供了构造方法,系统将不再提供

    • 如果这个时候,我们要使用无参构造方法,就必须自己给出。

    • 推荐:永远手动自己给出无参构造方法。

    •    构造方法也是可以重载的

    /*
    	我们一直在使用构造方法,但是,我们确没有定义构造方法,用的是哪里来的呢?
    	
    	构造方法的注意事项:
    		A:如果我们没有给出构造方法,系统将自动提供一个无参构造方法。
    		B:如果我们给出了构造方法,系统将不再提供默认的无参构造方法。
    			注意:这个时候,如果我们还想使用无参构造方法,就必须自己给出。建议永远自己给出无参构造方法
    		
    	给成员变量赋值有两种方式:
    		A:setXxx()
    		B:构造方法
    */
    
    class Student {
    	private String name;
    	private int age;
    
    	public Student() {
    		//System.out.println("我给了,你还给不");
    		System.out.println("这是无参构造方法");
    	}
    	
    	//构造方法的重载格式
    	public Student(String name) {
    		System.out.println("这是带一个String类型的构造方法");
    		this.name = name;
    	}
    	
    	public Student(int age) {
    		System.out.println("这是带一个int类型的构造方法");
    		this.age = age;
    	}
    	
    	public Student(String name,int age) {
    		System.out.println("这是一个带多个参数的构造方法");
    		this.name = name;
    		this.age = age;
    	}
    	
    	public void show() {
    		System.out.println(name+"---"+age);
    	}
    }
    
    class ConstructDemo2 {
    	public static void main(String[] args) {
    		//创建对象
    		Student s = new Student();
    		s.show();
    		System.out.println("-------------");
    		
    		//创建对象2
    		Student s2 = new Student("林青霞");
    		s2.show();
    		System.out.println("-------------");
    		
    		//创建对象3
    		Student s3 = new Student(27);
    		s3.show();
    		System.out.println("-------------");
    		
    		//创建对象4
    		Student s4 = new Student("林青霞",27);
    		s4.show();
    	}
    }

     

    类的成员方法

    成员方法其实就是我们前面讲过的方法

    方法具体划分:

    •    根据返回值

    •   有明确返回值方法

    •   返回void类型的方法

    •    根据形式参数

    •   无参方法

    •   带参方法

     

    一个基本类的标准代码写法

    •    成员变量

    •    构造方法

    •   无参构造方法

    •   带参构造方法

    •    成员方法

    •   getXxx()

    •   setXxx()

    给成员变量赋值的方式

    l  无参构造方法+setXxx()

    l  带参构造方法

     

    一个基本类的标准代码案例

    标准的学生类(讲解)

    •    感觉调用getXxx()输出学生信息比较麻烦,加入一个方法show(),输出学生对象的成员变量信息。

    class Student {
        private String name;
        private int age;
    
        public Student(){}
    
        public Student(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 StudentDemo {
        public static void main(String[] args) {
            //方式1
            Student s1 = new Student();
            s1.setName("Tom");
            s1.setAge(27);
            System.out.println(s1.getName()+"---"+s1.getAge());
    
            //方式2
            Student s2 = new Student("Jam",30);
            System.out.println(s2.getName()+"---"+s2.getAge());
        }
    }

     

    注意

    •    目前的代码是为了练习的一种标准格式

    •    给成员变量赋值有两种方式,可以只写一种

    •    如果不单独获取数据,可以不写getXxx()方法

     

    类的初始化过程

    Student s = newStudent();在内存中做了哪些事情?

    •    加载Student.class文件进内存

    •    在栈内存为s开辟空间

    •    在堆内存为学生对象开辟空间

    •    对学生对象的成员变量进行默认初始化

    •    对学生对象的成员变量进行显示初始化

    •    通过构造方法对学生对象的成员变量赋值

    •    学生对象初始化完毕,把对象地址赋值给s变量

     

    面向对象练习

    定义一个类Demo,其中定义一个求两个数据和的方法,定义一个测试了Test,进行测试。

    定义一个长方形类,定义求周长和面积的方法,然后定义一个测试了Test2,进行测试。

    定义一个员工类,自己分析出几个成员,然后给出成员变量,构造方法,getXxx()/setXxx()方法,以及一个显示所有成员信息的方法。并测试。

    定义一个类MyMath,提供基本的加减乘除功能,然后进行测试。

     


    九、static关键字

    可以修饰成员变量和成员方法

    static关键字特点

    •     随着类的加载而加载

    •     优先于对象存在

    •    被类的所有对象共享

            •    这也是我们判断是否使用静态关键字的条件

    •    可以通过类名调用:既可以通过对象名调用,也可以通过类名调用,建议通过类名调用

    static关键字注意事项

    •    在静态方法中是没有this关键字的

    •    静态方法只能访问静态的成员变量和静态的成员方法

    /*
    	static关键字注意事项
    		A:在静态方法中是没有this关键字的
    			如何理解呢?
    				静态是随着类的加载而加载,this是随着对象的创建而存在。
    				静态比对象先存在。
    		B:静态方法只能访问静态的成员变量和静态的成员方法
    				静态方法:
    					成员变量:只能访问静态变量
    					成员方法:只能访问静态成员方法
    				非静态方法:
    					成员变量:可以是静态的,也可以是非静态的
    					成员方法:可是是静态的成员方法,也可以是非静态的成员方法。
    			简单记:
    				静态只能访问静态。
    */
    class Teacher {
    	public int num = 10;
    	public static int num2 = 20;
    	
    	public void show() {
    		System.out.println(num); //隐含的告诉你访问的是成员变量
    		System.out.println(this.num); //明确的告诉你访问的是成员变量
    		System.out.println(num2);
    		
    		//function();
    		//function2();
    	}
    	
    	public static void method() {
    		//无法从静态上下文中引用非静态 变量 num
    		//System.out.println(num);
    		System.out.println(num2);
    		
    		//无法从静态上下文中引用非静态 方法 function()
    		//function();
    		function2();
    	}
    	
    	public void function() {
    	
    	}
    	
    	public static void function2() {
    	
    	}
    }
    
    class TeacherDemo {
    	public static void main(String[] args) {
    		//创建对象
    		Teacher t = new Teacher();
    		t.show();
    		System.out.println("------------");
    		t.method();
    	}
    }

     

    静态的内存图

    静态的内容存在于方法区的静态区

     

    静态变量和成员变量的区别

    l  所属不同

    •     静态变量属于类,所以也称为为类变量

    •     成员变量属于对象,所以也称为实例变量(对象变量)

    l  内存中位置不同

    •     静态变量存储于方法区的静态区

    •     成员变量存储于堆内存

    l  内存出现时间不同

    •     静态变量随着类的加载而加载,随着类的消失而消失

    •     成员变量随着对象的创建而存在,随着对象的消失而消失

    l  调用不同

    •     静态变量可以通过类名调用,也可以通过对象调用

    •     成员变量只能通过对象名调用

     

    main方法是静态的

    public static voidmain(String[] args) {}

    •    public jvm调用,访问权限足够大。

    •    static jvm调用,不用创建对象,直接类名访问

    •    voidjvm调用,不需要给jvm返回值

    •    main 一个通用的名称,虽然不是关键字,但是被jvm识别

    •    String[] args以前用于接收键盘录入的

    •   演示案例

    /*
    	main方法的格式讲解:
    		public static void main(String[] args) {...}
    		
    		public:公共的,访问权限是最大的。由于main方法是被jvm调用,所以权限要够大。
    		static:静态的,不需要创建对象,通过类名就可以。方便jvm的调用。
    		void:因为我们曾经说过,方法的返回值是返回给调用者,而main方法是被jvm调用。你返回内容给jvm没有意义。
    		main:是一个常见的方法入口。我见过的语言都是以main作为入口。
    		String[] args:这是一个字符串数组。值去哪里了?
    			这个东西到底有什么用啊?怎么给值啊?
    				这个东西早期是为了接收键盘录入的数据的。
    				格式是:
    					java MainDemo hello world java
    */
    class MainDemo {
    	public static void main(String[] args) {
    		//System.out.println(args); //[Ljava.lang.String;@175078b
    		//System.out.println(args.length); //0
    		//System.out.println(args[0]); //ArrayIndexOutOfBoundsException
    		
    		//接收数据后
    		System.out.println(args); 
    		System.out.println(args.length); 
    		//System.out.println(args[0]); 
    		for(int x=0; x<args.length; x++) {
    			System.out.println(args[x]);
    		}
    	}
    }

     

    制作帮助文档

    制作工具类

    •    ArrayTools

    制作帮助文档(API)

    •    javadoc -d 目录 -author -version ArrayTool.java

    class ArrayTool {
    	
    	//把构造方法私有,外界就不能在创建对象了
    	private ArrayTool(){}
    
    	public static void printArray(int[] arr) {
    		for(int x=0; x<arr.length; x++) {
    			if(x == arr.length-1) {
    				System.out.println(arr[x]);
    			}else {
    				System.out.print(arr[x]+", ");
    			}
    		}
    	}
    }

     

    如何使用帮助文档

    l  找到文档,打开文档

    l  点击显示,找到索引,出现输入框

    l  你应该知道你找谁?举例:Scanner

    l  看这个类的结构(需不需要导包)

    •     成员变量字段

    •     构造方法构造方法

    •     成员方法方法

    l  看这个类的说

    l  看构造方法

    l  看成员方法

    l  然后使用

     

    通过API学习Math

    Math类概述

    •    Math包含用于执行基本数学运算的方法

    Math类特点

    •    没有构造方法,因为成员都是静态的

    Math类讲解一个方法

    •    获取随机数

    • public static double random(): [0.0,1.0)

    •    获取1-100之间的随机数

    • int number = (int)(Math.random()*100)+1;

    案例:

    •    猜数字小游戏

     

    代码块

    代码块

    •    Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)

    •    局部代码块

    •   在方法中出现;限定变量生命周期,及早释放,提高内存利用率

    •    构造代码块

    •   在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行

    •    静态代码块在类中方法外出现,加了static修饰

    •   在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且值执行一次。

    /*
    	代码块:在Java中,使用{}括起来的代码被称为代码块。
    	根据其位置和声明的不同,可以分为
    		局部代码块:局部位置,用于限定变量的生命周期。
    		构造代码块:在类中的成员位置,用{}括起来的代码。每次调用构造方法执行前,都会先执行构造代码块。
    			作用:可以把多个构造方法中的共同代码放到一起,对对象进行初始化。
    		静态代码块:在类中的成员位置,用{}括起来的代码,只不过它用static修饰了。
    			作用:一般是对类进行初始化。
    			
    	面试题?
    		静态代码块,构造代码块,构造方法的执行顺序?
    		静态代码块 -- 构造代码块 -- 构造方法
    		静态代码块:只执行一次
    		构造代码块:每次调用构造方法都执行
    */
    class Code {
    	static {
    		int a = 1000;
    		System.out.println(a);
    	}
    
    	//构造代码块
    	{
    		int x = 100;
    		System.out.println(x);
    	}
    	
    	//构造方法
    	public Code(){
    		System.out.println("code");
    	}
    	
    	//构造方法
    	public Code(int a){
    		System.out.println("code");
    	}
    	
    	//构造代码块
    	{
    		int y = 200;
    		System.out.println(y);
    	}
    	
    	//静态代码块
    	static {
    		int b = 2000;
    		System.out.println(b);
    	}
    }
    
    class CodeDemo {
    	public static void main(String[] args) {
    		//局部代码块
    		{
    			int x = 10;
    			System.out.println(x);
    		}
    		//找不到符号
    		//System.out.println(x);
    		{
    			int y = 20;
    			System.out.println(y);
    		}
    		System.out.println("---------------");
    		
    		Code c = new Code();	
    		System.out.println("---------------");
    		Code c2 = new Code();
    		System.out.println("---------------");
    		Code c3 = new Code(1);
    	}
    }

    进阶内容

    继承

    多态

    抽象类

    接口

    包和导包

    权限修饰符

    内部类


    十、继承

    继承概述

    •    多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

    •    通过extends关键字可以实现类与类的继承

          •   class 子类名  extends 父类名{} 

    •    单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。

    •    有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。

     

    继承的好处

    继承的好处

    •    提高了代码的复用性

    •   多个类相同的成员可以放到同一个类中

    •    提高了代码的维护性

    •   如果功能的代码需要修改,修改一处即可

    •    让类与类之间产生了关系,是多态的前提

       • 其实这也是继承的一个弊端:类的耦合性很强

    • 让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。

    • 原则:低耦合,高内聚。

    • 耦合:类与类的关系

    • 内聚:自己完成某件事情的能力

     

    Java中继承的特点

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

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

    •    class SubDemo extendsDemo{} //ok

    •    class SubDemo extendsDemo1,Demo2...//error

    Java支持多层继承(继承体系)

    •    class A{}

    •    class B extends A{}

    •    class C extends B{}

     

    Java中继承的注意事项

    子类只能继承父类所有非私有的成员(成员方法和成员变量)

    •    其实这也体现了继承的另一个弊端:打破了封装性

    子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。

    不要为了部分功能而去继承

    我们到底在什么时候使用继承呢?

    •    继承中类之间体现的是:isa的关系。

     

    继承中成员变量的关系

    案例演示

    •    子父类中同名和不同名的成员变量

    结论:

    •    在子类方法中访问一个变量

    •   首先在子类局部范围找

    •   然后在子类成员范围找

    •   最后在父类成员范围找(肯定不能访问到父类局部范围)

    •   如果还是没有就报错。(不考虑父亲的父亲)

     

    super关键字

    super的用法和this很像

    •    this代表本类对应的引用。

    •    super代表父类存储空间的标识(可以理解为父类引用)

    用法(thissuper均可如下使用)

    • l  访问成员变量

    •   this.成员变量     super.成员变量

    • l  访问构造方法(子父类的构造方法问题讲)

    •  this()     super()

    • l  访问成员方法(子父类的成员方法问题讲)

    •   this.成员方法()   super.成员方法()

    /*
    	问题是:
    		我不仅仅要输出局部范围的num,还要输出本类成员范围的num。怎么办呢?
    		我还想要输出父类成员范围的num。怎么办呢?
    			如果有一个东西和this相似,但是可以直接访问父类的数据就好了。
    			恭喜你,这个关键字是存在的:super。
    			
    	this和super的区别?
    		分别是什么呢?
    			this代表本类对应的引用。
    			super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)
    
    		怎么用呢?
    			A:调用成员变量
    				this.成员变量 调用本类的成员变量
    				super.成员变量 调用父类的成员变量
    			B:调用构造方法
    				this(...)	调用本类的构造方法
    				super(...)	调用父类的构造方法
    			C:调用成员方法
    				this.成员方法 调用本类的成员方法
    				super.成员方法 调用父类的成员方法
    */
    class Father {
    	public int num = 10;
    }
    
    class Son extends Father {
    	public int num = 20;
    	
    	public void show() {
    		int num = 30;
    		System.out.println(num);
    		System.out.println(this.num);
    		System.out.println(super.num);
    	}
    }
    
    class ExtendsDemo5 {
    	public static void main(String[] args) {
    		Son s = new Son();
    		s.show();
    	}
    }

     

    继承中构造方法的关系

    子类中所有的构造方法默认都会访问父类中空参数的构造方法

    为什么呢?

    •    因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。

    •     每一个构造方法的第一条语句默认都是:super()

    /*
    	继承中构造方法的关系
    		A:子类中所有的构造方法默认都会访问父类中空参数的构造方法
    		B:为什么呢?
    			因为子类会继承父类中的数据,可能还会使用父类的数据。
    			所以,子类初始化之前,一定要先完成父类数据的初始化。
    			
    			注意:子类每一个构造方法的第一条语句默认都是:super();
    */
    class Father {
    	int age;
    
    	public Father() {
    		System.out.println("Father的无参构造方法");
    	}
    	
    	public Father(String name) {
    		System.out.println("Father的带参构造方法");
    	}
    }
    
    class Son extends Father {
    	public Son() {
    		//super();
    		System.out.println("Son的无参构造方法");
    	}
    	
    	public Son(String name) {
    		//super();
    		System.out.println("Son的带参构造方法");
    	}
    }	
    
    class ExtendsDemo6 {
    	public static void main(String[] args) {
    		//创建对象
    		Son s = new Son();
    		System.out.println("------------");
    		Son s2 = new Son("林青霞");
    	}
    }

     

    继承中构造方法的关系

    如何父类中没有构造方法,该怎么办呢?

    •    子类通过super去显示调用父类其他的带参的构造方法

    •    子类通过this去调用本类的其他构造方法

    •   本类其他构造也必须首先访问了父类构造

    •    一定要注意:

    •   super()或者this(.)必须出现在第一条语句上

    •   否则,就会有父类数据的多次初始化

    /*
    	如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢?
    		报错。
    	如何解决呢?	
    		A:在父类中加一个无参构造方法
    		B:通过使用super关键字去显示的调用父类的带参构造方法
    		C:子类通过this去调用本类的其他构造方法
    			子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。
    			
    	注意事项:
    		this(...)或者super(...)必须出现在第一条语句上。
    		如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。
    */
    class Father {
    	/*
    	public Father() {
    		System.out.println("Father的无参构造方法");
    	}
    	*/
    	
    	public Father(String name) {
    		System.out.println("Father的带参构造方法");
    	}
    }
    
    class Son extends Father {
    	public Son() {
    		super("随便给");
    		System.out.println("Son的无参构造方法");
    		//super("随便给");
    	}
    	
    	public Son(String name) {
    		//super("随便给");
    		this();
    		System.out.println("Son的带参构造方法");
    	}
    }
    
    class ExtendsDemo7 {
    	public static void main(String[] args) {
    		Son s = new Son();
    		System.out.println("----------------");
    		Son ss = new Son("林青霞");
    	}
    }

     

    继承中成员方法的关系

    案例演示

    •    子父类中同名和不同名的成员方法

    结论:

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

    •   首先在子类中找

    •   然后在父类中找

    •   如果还是没有就报错。(不考虑父亲的父亲)

     

    继承中成员方法的关系

    方法重写概述

    •    子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。

    •    使用特点:

    •   如果方法名不同,就调用对应的方法

    •   如果方法名相同,最终使用的是子类自己的

    方法重写的应用:

    •     当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

    *
    	方法重写:子类中出现了和父类中方法声明一模一样的方法。
    	
    	方法重载:
    		本类中出现的方法名一样,参数列表不同的方法。与返回值无关。
    
    	子类对象调用方法的时候:
    		先找子类本身,再找父类。
    		
    	方法重写的应用:
    		当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。
    		这样,即沿袭了父类的功能,又定义了子类特有的内容。
    		
    	案例:
    		A:定义一个手机类。
    		B:通过研究,我发明了一个新手机,这个手机的作用是在打完电话后,可以听天气预报。
    		按照我们基本的设计,我们把代码给写出来了。
    		但是呢?我们又发现新手机应该是手机,所以,它应该继承自手机。
    		其实这个时候的设计,并不是最好的。
    		因为手机打电话功能,是手机本身就具备的最基本的功能。
    		所以,我的新手机是不用在提供这个功能的。
    		但是,这个时候,打电话功能就没有了。这个不好。
    		最终,还是加上这个功能。由于它继承了手机类,所以,我们就直接使用父类的功能即可。
    		那么,如何使用父类的功能呢?通过super关键字调用
    */
    class Phone {
    	public void call(String name) {
    		System.out.println("给"+name+"打电话");
    	}
    }
    
    class NewPhone extends Phone {
    	public void call(String name) {
    		//System.out.println("给"+name+"打电话");
    		super.call(name);
    		System.out.println("可以听天气预报了");
    	}
    }
    
    class ExtendsDemo9 {
    	public static void main(String[] args) {
    		NewPhone np = new NewPhone();
    		np.call("林青霞");
    	}
    }

     

    继承中成员方法的关系

    方法重写的注意事项

    •    父类中私有方法不能被重写

    •    子类重写父类方法时,访问权限不能更低

    •    父类静态方法,子类也必须通过静态方法进行重写。(其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中会讲解)

    /*
    	方法重写的注意事项
    		A:父类中私有方法不能被重写
    			因为父类私有方法子类根本就无法继承
    		B:子类重写父类方法时,访问权限不能更低
    			最好就一致
    		C:父类静态方法,子类也必须通过静态方法进行重写
    			其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解
    			
    		子类重写父类方法的时候,最好声明一模一样。
    */
    class Father {
    	//private void show() {}
    	
    	/*
    	public void show() {
    		System.out.println("show Father");
    	}
    	*/
    	
    	void show() {
    		System.out.println("show Father");
    	}
    	/*
    	public static void method() {
    		
    	}
    	*/
    	
    	public void method() {
    		
    	}
    }
    
    class Son extends Father {
    	//private void show() {}
    
    	/*
    	public void show() {
    		System.out.println("show Son");
    	}
    	*/
    	
    	public void show() {
    		System.out.println("show Son");
    	}
    	
    	
    	public static void method() {
    	
    	}
    	
    	/*
    	public void method() {
    	
    	}
    	*/
    }
    
    class ExtendsDemo10 {
    	public static void main(String[] args) {
    		Son s = new Son();
    		s.show();
    	}
    }

     

    两个面试题

    方法重写和方法重载的区别?方法重载能改变返回值类型吗?

    •    Overload:方法重载 

    •    Override:方法重写

    • 方法重写:在子类中,出现和父类中一模一样的方法声明的现象。

    • 方法重载:同一个类中,出现的方法名相同,参数列表不同的现象。

    • 方法重载能改变返回值类型,因为它和返回值类型无关。
       

    this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。

    /*
    	看程序写结果:
    		A:成员变量	就近原则
    		B:this和super的问题
    			this访问本类的成员
    			super访问父类的成员
    		C:子类构造方法执行前默认先执行父类的无参构造方法
    		D:一个类的初始化过程
    			成员变量进行初始化
    				默认初始化
    				显示初始化
    				构造方法初始化
    				
    	结果:
    		fu
    		zi
    		30
    		20
    		10
    */
    class Fu{
    	public int num = 10;
    	public Fu(){
    		System.out.println("fu");
    	}
    }
    class Zi extends Fu{
    	public int num = 20;
    	public Zi(){
    		System.out.println("zi");
    	}
    	public void show(){
    		int num = 30;
    		System.out.println(num); //30
    		System.out.println(this.num); //20
    		System.out.println(super.num); //10
    	}
    }
    class ExtendsTest {
    	public static void main(String[] args) {
    		Zi z = new Zi();
    		z.show();
    	}
    }

     

    继承练习

    • 数据初始化的面试题

    • A:一个类的初始化过程

    • B:子父类的构造执行过程

    • C:分层初始化

    /*
    	看程序写结果:
    		A:成员变量的问题
    			int x = 10; //成员变量是基本类型
    			Student s = new Student(); //成员变量是引用类型
    		B:一个类的初始化过程
    			成员变量的初始化
    				默认初始化
    				显示初始化
    				构造方法初始化
    		C:子父类的初始化(分层初始化)
    			先进行父类初始化,然后进行子类初始化。
    			
    	结果:
    		YXYZ
    		
    	问题:
    		虽然子类中构造方法默认有一个super()
    		初始化的时候,不是按照那个顺序进行的。
    		而是按照分层初始化进行的。
    		它仅仅表示要先初始化父类数据,再初始化子类数据。
    */
    class X {
    	Y b = new Y();
    	X() {
    		System.out.print("X");
    	}
    }
    
    class Y {
    	Y() {
    		System.out.print("Y");
    	}
    }
    
    public class Z extends X {
    	Y y = new Y();
    	Z() {
    		//super
    		System.out.print("Z");
    	}
    	public static void main(String[] args) {
    		new Z(); 
    	}
    }

     

    final关键字

    final关键字是最终的意思,可以修饰类,成员变量,成员方法。

    •    修饰类,类不能被继承

    •    修饰变量,变量就变成了常量,只能被赋值一次

    •    修饰方法,方法不能被重写

    /*
    	final可以修饰类,方法,变量
    	
    	特点:
    		final可以修饰类,该类不能被继承。
    		final可以修饰方法,该方法不能被重写。(覆盖,复写)
    		final可以修饰变量,该变量不能被重新赋值。因为这个变量其实常量。
    		
    	常量:
    		A:字面值常量
    			"hello",10,true
    		B:自定义常量
    			final int x = 10;
    */
    
    //final class Fu //无法从最终Fu进行继承
    
    class Fu {
    	public int num = 10;
    	public final int num2 = 20;
    
    	/*
    	public final void show() {
    	
    	}
    	*/
    }
    
    class Zi extends Fu {
    	// Zi中的show()无法覆盖Fu中的show()
    	public void show() {
    		num = 100;
    		System.out.println(num);
    		
    		//无法为最终变量num2分配值
    		//num2 = 200;
    		System.out.println(num2);
    	}
    }
    
    class FinalDemo {
    	public static void main(String[] args) {
    		Zi z = new Zi();
    		z.show();
    	}
    }

     

    final关键字面试题

    •    final修饰局部变量

    •   在方法内部,该变量不可以被改变

    •   在方法声明上,分别演示基本类型和引用类型作为参数的情况

    •   基本类型,是值不能被改变

    •   引用类型,是地址值不能被改变

    /*
    	面试题:final修饰局部变量的问题
    		基本类型:基本类型的值不能发生改变。
    		引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
    */
    class Student {
    	int age = 10;
    }
    
    class FinalTest {
    	public static void main(String[] args) {
    		//局部变量是基本数据类型
    		int x = 10;
    		x = 100;
    		System.out.println(x);
    		final int y = 10;
    		//无法为最终变量y分配值
    		//y = 100;
    		System.out.println(y);
    		System.out.println("--------------");
    		
    		//局部变量是引用数据类型
    		Student s = new Student();
    		System.out.println(s.age);
    		s.age = 100;
    		System.out.println(s.age);
    		System.out.println("--------------");
    		
    		final Student ss = new Student();
    		System.out.println(ss.age);
    		ss.age = 100;
    		System.out.println(ss.age);
    		
    		//重新分配内存空间
    		//无法为最终变量ss分配值
    		ss = new Student();
    	}
    }

    •    final修饰变量的初始化时机

    •   在对象构造完毕前即可

    /*
    	final修饰变量的初始化时机
    		A:被final修饰的变量只能赋值一次。
    		B:在构造方法完毕前。(非静态的常量)
    */
    class Demo {
    	//int num = 10;
    	//final int num2 = 20;
    	
    	int num;
    	final int num2;
    	
    	{
    		//num2 = 10;
    	}
    	
    	public Demo() {
    		num = 100;
    		//无法为最终变量num2分配值
    		num2 = 200;
    	}
    }
    
    class FinalTest2 {
    	public static void main(String[] args) {
    		Demo d = new Demo();
    		System.out.println(d.num);
    		System.out.println(d.num2);
    	}
    }

     


    十一、多态

    多态概述

    •    某一个事物,在不同时刻表现出来的不同状态。

    •    举例:

    •   猫可以是猫的类型。猫 m = new ();

    •   同时猫也是动物的一种,也可以把猫称为动物。

    •   动物 d = new ();

    •   在举一个例子:水在不同时刻的状态

    l  多态前提和体现

    •     有继承关系或者实现关系

    •     有方法重写 

    •     有父类或者父类接口引用指向子类对象

    |  多态的分类

    具体类多态

    • class Fu {}

    • class Zi extends Fu {}

    • Fu f = new Zi();

    抽象类多态

    • abstract class Fu {}

    • class Zi extends Fu {}

    • Fu f = new Zi();

    接口多态

    • interface Fu {}

    • class Zi implements Fu {}

    • Fu f = new Zi();

     

    多态案例及成员访问特点

    多态案例

    •    按照前提写一个多态的案例

    成员访问特点

    l  成员变量

    l  编译看左边,运行看左边

    |  构造方法

    | 子类的构造都会默认访问父类构造

    l  成员方法

    l  编译看左边,运行看右边

    l  静态方法

    l  编译看左边,运行看左边

    l  所以前面静态方法不能算方法的重写

     

    多态的好处和弊端

    多态的好处

    •    提高了程序的维护性(由继承保证)

    •    提高了程序的扩展性(由多态保证)

    多态的弊端

    •    不能访问子类特有功能

    •    那么我们如何才能访问子类的特有功能呢?

    •   多态中的转型

     

    多态中的转型问题

    向上转型

    •    从子到父

    •    父类引用指向子类对象

    向下转型

    •    从父到子

    •    父类引用转为子类对象

    /*
    	多态的弊端:
    		不能使用子类的特有功能。
    		
    	我就想使用子类的特有功能?行不行?
    		行。
    		
    	怎么用呢?
    		A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
    		B:把父类的引用强制转换为子类的引用。(向下转型)
    		
    	对象间的转型问题:
    		向上转型:
    			Fu f = new Zi();
    		向下转型:
    			Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。
    */
    class Fu {
    	public void show() {
    		System.out.println("show fu");
    	}
    }
    
    class Zi extends Fu {
    	public void show() {
    		System.out.println("show zi");
    	}
    	
    	public void method() {
    		System.out.println("method zi");
    	}
    
    }
    
    class DuoTaiDemo4 {
    	public static void main(String[] args) {
    		//测试
    		Fu f = new Zi();
    		f.show();
    		//f.method();
    		
    		//创建子类对象
    		//Zi z = new Zi();
    		//z.show();
    		//z.method();
    		
    		//你能够把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢?
    		//如果可以,但是如下
    		Zi z = (Zi)f;
    		z.show();
    		z.method();
    	}
    }

     

    多态练习

    /*
    	看程序写结果:先判断有没有问题,如果没有,写出结果
    	
    	多态的成员访问特点:
    		方法:编译看左边,运行看右边。
    		
    	继承的时候:
    		子类中有和父类中一样的方法,叫重写。
    		子类中没有父亲中出现过的方法,方法就被继承过来了。
    */
    class A {
    	public void show() {
    		show2();
    	}
    	public void show2() {
    		System.out.println("我");
    	}
    }
    class B extends A {
    	/*
    	public void show() {
    		show2();
    	}
    	*/
    
    	public void show2() {
    		System.out.println("爱");
    	}
    }
    class C extends B {
    	public void show() {
    		super.show();
    	}
    	public void show2() {
    		System.out.println("你");
    	}
    }
    public class DuoTaiTest4 {
    	public static void main(String[] args) {
    		A a = new B();
    		a.show();
    		
    		B b = new C();
    		b.show();
    	}
    }

    十二、抽象类

    抽象类概述

    •    回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。

        为什么呢?因为,我说动物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。

     

    抽象类概述

    抽象类概述

    •    回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。

        为什么呢?因为,我说动物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。

     

    抽象类特点

    抽象类特点

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

    •   格式

    •   abstract class 类名{}

    •   public abstract void  eat();

    •    抽象类不一定有抽象方法,有抽象方法的类一定是抽象类

    •    抽象类不能实例化

    •   那么,抽象类如何实例化呢?

    •   按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。

    •    抽象类的子类

    •   要么是抽象类

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

     

    抽象类的成员特点

    成员变量

    •    可以是变量

    •    也可以是常量

    构造方法

    •    有构造方法,但是不能实例化

    •    那么,构造方法的作用是什么呢?

    •   用于子类访问父类数据的初始化

    成员方法

    •    可以有抽象方法限定子类必须完成某些动作

    •    也可以有非抽象方法提高代码服用性

     

    抽象类练习

    猫狗案例

    •     具体事物:猫,狗

    •     共性:姓名,年龄,吃饭

    老师案例

    •      具体事物:基础班老师,就业班老师

    •     共性:姓名,年龄,讲课。

    学生案例

    •      具体事务:基础班学员,就业班学员

    •     共性:姓名,年龄,班级,学习,吃饭

    员工案例(备注部分)

     

    抽象类的几个小问题

    一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?

    abstract不能和哪些关键字共存

    •     private    冲突

    •     final    冲突 

    •      static  无意义

    /*
    一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
    	A:可以。
    	B:不让创建对象。
    
    abstract不能和哪些关键字共存?
    	private	冲突
    	final	冲突	
    	static	无意义
    */
    abstract class Fu {
    	//public abstract void show();
    	//非法的修饰符组合: abstract和private
    	//private abstract void show();
    	
    	//非法的修饰符组合
    	//final abstract void show();	
    	
    	//非法的修饰符组合
    	static abstract void show();
    	
    	public static void method() {
    		System.out.println("method");
    	}
    }
    
    class Zi extends Fu {
    	public void show() {}
    }
    
    class AbstractDemo3 {
    	public static void main(String[] args) {
    		Fu.method();
    	}
    }

     


    十三、接口

    接口概述

    •    继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了,对不。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的,对不。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被培训,只需要这部分猫狗把这些额外功能实现即可。

     

    接口特点

    接口特点

    •    接口用关键字interface表示

    •   格式:interface 接口名{}

    •    类实现接口用implements表示

    •   格式:class  类名  implements  接口名{}

    •    接口不能实例化

    •   那么,接口如何实例化呢?

    •   按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。

    •    接口的子类

    •   要么是抽象类

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

     

    接口成员特点

    成员变量

    •    只能是常量

    •    默认修饰符public static final

    构造方法

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

    成员方法

    •    只能是抽象方法

    •    默认修饰符public abstract

    /*
    	接口成员特点
    		成员变量;只能是常量,并且是静态的。
    				默认修饰符:public static final
    				建议:自己手动给出。
    		构造方法:接口没有构造方法。
    		成员方法:只能是抽象方法。
    				默认修饰符:public abstract
    				建议:自己手动给出。
    		
    	所有的类都默认继承自一个类:Object。
    	类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
    */
    interface Inter {
    	public int num = 10;
    	public final int num2 = 20;
    	public static final int num3 = 30;
    	
    	//错误: 需要<标识符>
    	//public Inter() {}
    	
    	//接口方法不能带有主体
    	//public void show() {}
    
    	//abstract void show(); //默认public
    	public void show(); //默认abstract
    }
    
    //接口名+Impl这种格式是接口的实现类格式
    /*
    class InterImpl implements Inter {
    	public InterImpl() {
    		super();
    	}
    }
    */
    
    class InterImpl extends Object implements Inter {
    	public InterImpl() {
    		super();
    	}
    	
    	public void show() {}
    }
    
    //测试类
    class InterfaceDemo2 {
    	public static void main(String[] args) {
    		//创建对象
    		Inter i = new InterImpl();
    		System.out.println(i.num);
    		System.out.println(i.num2);
    		//i.num = 100;
    		//i.num2 = 200;
    		//System.out.println(i.num); //无法为最终变量num分配值
    		//System.out.println(i.num2);//无法为最终变量num2分配值
    		System.out.println(Inter.num);
    		System.out.println(Inter.num2);
    		System.out.println("--------------");
    	}
    }

     

    类与类,类与接口以及接口与接口的关系

    类与类

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

    类与接口

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

    接口与接口

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

    /*
    	类与类:
    		继承关系,只能单继承,可以多层继承。
    	类与接口:
    		实现关系,可以单实现,也可以多实现。
    		并且还可以在继承一个类的同时实现多个接口。
    	接口与接口:
    		继承关系,可以单继承,也可以多继承。
    */
    interface Father {
    	public abstract void show();
    }
    
    interface Mother {
    	public abstract void show2();
    }
    
    interface Sister extends Father,Mother {
    
    }
    
    //class Son implements Father,Mother //多实现
    class Son extends Object implements Father,Mother {
    	public void show() {
    		System.out.println("show son");
    	}
    	
    	public void show2() {
    		System.out.println("show2 son");
    	}
    }
    
    class InterfaceDemo3 {
    	public static void main(String[] args) {
    		//创建对象
    		Father f = new Son();
    		f.show();
    		//f.show2(); //报错
    	
    		Mother m = new Son();
    		//m.show(); //报错
    		m.show2();
    	}
    }

     

    抽象类和接口的区别

    成员区别

    •    抽象类变量,常量;有抽象方法;抽象方法,非抽象方法

    •    接口常量;抽象方法

    关系区别

    •    类与类继承,单继承

    •    类与接口实现,单实现,多实现

    •    接口与接口继承,单继承,多继承

    设计理念区别

    •    抽象类被继承体现的是:is a的关系。共性功能

    •    接口被实现体现的是:like a的关系。扩展功能

     

    接口练习

    猫狗案例,加入跳高的额外功能

    老师和学生案例,加入抽烟的额外功能

    /*
    	猫狗案例,加入跳高的额外功能
    	
    	分析:从具体到抽象
    		猫:
    			姓名,年龄
    			吃饭,睡觉
    		狗:
    			姓名,年龄
    			吃饭,睡觉
    			
    		由于有共性功能,所以,我们抽取出一个父类:
    		动物:
    			姓名,年龄
    			吃饭();
    			睡觉(){}
    			
    		猫:继承自动物
    		狗:继承自动物
    		
    		跳高的额外功能是一个新的扩展功能,所以我们要定义一个接口
    		接口:
    			跳高
    			
    		部分猫:实现跳高
    		部分狗:实现跳高
    	实现;
    		从抽象到具体
    		
    	使用:
    		使用具体类
    */
    //定义跳高接口
    interface Jumpping {
    	//跳高功能
    	public abstract void jump();
    }
    
    //定义抽象类
    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 void sleep() {
    		System.out.println("睡觉觉了");
    	}
    }
    
    //具体猫类
    class Cat extends Animal {
    	public Cat(){}
    	
    	public Cat(String name,int age) {
    		super(name,age);
    	}
    	
    	public void eat() {
    		System.out.println("猫吃鱼");
    	}
    }
    
    //具体狗类
    class Dog extends Animal {
    	public Dog(){}
    	
    	public Dog(String name,int age) {
    		super(name,age);
    	}
    	
    	public void eat() {
    		System.out.println("狗吃肉");
    	}
    }
    
    //有跳高功能的猫
    class JumpCat extends Cat implements Jumpping {
    	public JumpCat() {}
    	
    	public JumpCat(String name,int age) {
    		super(name,age);
    	}
    
    	public void jump() {
    		System.out.println("跳高猫");
    	}
    }
    
    //有跳高功能的狗
    class JumpDog extends Dog implements Jumpping {
    	public JumpDog() {}
    	
    	public JumpDog(String name,int age) {
    		super(name,age);
    	}
    
    	public void jump() {
    		System.out.println("跳高狗");
    	}
    }
    
    class InterfaceTest {
    	public static void main(String[] args) {
    		//定义跳高猫并测试
    		JumpCat jc = new JumpCat();
    		jc.setName("哆啦A梦");
    		jc.setAge(3);
    		System.out.println(jc.getName()+"---"+jc.getAge());
    		jc.eat();
    		jc.sleep();
    		jc.jump();
    		System.out.println("-----------------");
    		
    		JumpCat jc2 = new JumpCat("加菲猫",2);
    		System.out.println(jc2.getName()+"---"+jc2.getAge());
    		jc2.eat();
    		jc2.sleep();
    		jc2.jump();
    		
    		//定义跳高狗并进行测试的事情自己完成。
    	}
    }

     

    形式参数和返回值问题案例

    形式参数

    •    基本类型

    •    引用类型

    /*
    	形式参数:
    		基本类型(太简单)
    		引用类型
    			类名:(匿名对象的时候其实我们已经讲过了)需要的是该类的对象
    			抽象类:需要的是该抽象的类子类对象
    			接口
    */
    abstract class Person {
    	public abstract void study();
    }
    
    class PersonDemo {
    	public void method(Person p) {//p; p = new Student();  Person p = new Student(); //多态
    		p.study();
    	}
    }
    
    //定义一个具体的学生类
    class Student extends Person {
    	public void study() {
    		System.out.println("Good Good Study,Day Day Up");
    	}
    }
    
    class PersonTest {
    	public static void main(String[] args) {
    		//目前是没有办法的使用的
    		//因为抽象类没有对应的具体类
    		//那么,我们就应该先定义一个具体类
    		//需求:我要使用PersonDemo类中的method()方法
    		PersonDemo pd = new PersonDemo();
    		Person p = new Student();
    		pd.method(p);
    	}
    }

    返回值类型

    •    基本类型

    •    引用类型

    /*
    	返回值类型
    		基本类型:(基本类型太简单)
    		引用类型:
    			类:返回的是该类的对象
    			抽象类:返回的是该抽象类的子类对象
    			接口:返回的是该接口的实现类的对象
    */
    //定义一个爱好的接口
    interface Love {
    	public abstract void love();
    }
    
    class LoveDemo {
    	public Love getLove() {
    		//Love l = new Teacher();
    		//return l;
    		
    		return new Teacher();
    	}
    }
    
    //定义具体类实现接口
    class Teacher implements Love {
    	public void love() {
    		System.out.println("老师爱学生,爱Java,爱林青霞");
    	}
    }
    
    class TeacherTest2 {
    	public static void main(String[] args) {
    		//如何测试呢?
    		LoveDemo ld = new LoveDemo();
    		Love l = ld.getLove(); //new Teacher(); Love l = new Teacher(); 多态
    		l.love();
    	}
    }

    链式编程

    • 对象.方法1().方法2().......方法n();

    • 这种用法:其实在方法1()调用完毕后,应该一个对象;

    •    方法2()调用完毕后,应该返回一个对象。

    •    方法n()调用完毕后,可能是对象,也可以不是对象。 

    /*
    	链式编程。
    		每次调用完毕方法后,返回的是一个对象。
    */
    class Student {
    	public void study() {
    		System.out.println("Good Good Study,Day Day Up");
    	}
    }
    
    class StudentDemo {
    	public Student getStudent() {
    		return new Student();
    	}
    }
    
    class StudentTest3 {
    	public static void main(String[] args) {
    		//如何调用的呢?
    		StudentDemo sd = new StudentDemo();
    		//Student s = sd.getStudent();
    		//s.study();
    		
    		//大家注意了
    		sd.getStudent().study();
    	}
    }

    十四、包

    包的概述

    •    其实就是文件夹

    •    作用:对类进行分类管理

    • A:区分同名的类

    • B:对类进行分类管理

    • a:按照功能分

    • b:按照模块分

    •    包的划分:

    •   举例:

    •   学生的增加,删除,修改,查询

    •   老师的增加,删除,修改,查询

    •   以及以后可能出现的其他的类的增加,删除,修改,查询

    •   基本的划分:按照模块和功能分。

    •   高级的划分:做项目的时候就能看到了。

     

    包的定义及注意事项

    定义包的格式

    •    package 包名;

    •   多级包用.分开即可

    •    注意事项:

    •   package语句必须是程序的第一条可执行的代码

    •   package语句在一个java文件中只能有一个

    •   如果没有package,默认表示无包名

     

    带包的类的编译和运行

    手动式

    •     a:javac编译当前类文件。

    •     b:手动建立包对应的文件夹。

    •     c:a步骤的class文件放到b步骤的最终文件夹下。

    •     d:通过java命令执行。注意了:需要带包名称的执行

    •   java cn.itcast.HelloWorld

    自动式

    •     a:javac编译的时候带上-d即可

    •   javac -d .HelloWorld.java

    •     b:通过java命令执行。和手动式一样

     

    不同包下类之间的访问

    定义两个类:Demo,Test

    •    Demo

    •   求和方法(sum)

    •    Test

    •    测试方法(main)

     

    导包

    导包概述

    •    不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。

    导包格式

    •    import 包名;

    •    注意:

    •   这种方式导入是到类的名称。

    •   虽然可以最后写*,但是不建议。

    package,import,class有没有顺序关系(面试题)

    • package > import > class

     

    /*
    	Test类,测试
    
    	导包:
    		格式:import 包名;
    			这种方式导入是到类的名称。
    		注意:我们用谁就导谁。
    		
    	面试题:
    		package,import,class有没有顺序关系?
    		有。
    		package > import > class
    		
    		Package:只能有一个
    		import:可以有多个
    		class:可以有多个,以后建议是一个
    */
    package cn.itcast;
    
    import com.liuyi.Demo;
    
    class Test {
    	public static void main(String[] args) {
    		//Demo d = new Demo();
    		/*
    		com.liuyi.Demo d = new com.liuyi.Demo();
    		System.out.println(d.sum(10,20));
    		
    		com.liuyi.Demo d2 = new com.liuyi.Demo();
    		System.out.println(d2.sum(10,20));
    		
    		com.liuyi.Demo d3 = new com.liuyi.Demo();
    		System.out.println(d3.sum(10,20));
    		
    		com.liuyi.Demo d4 = new com.liuyi.Demo();
    		System.out.println(d4.sum(10,20));
    		*/
    		
    		Demo d = new Demo();
    		System.out.println(d.sum(10,20));
    	}
    }
    
    /*
    	第一个问题:找不到Demo
    	
    	第二个问题:程序包com.liuyi不存在
    	
    	第三个问题: Demo在com.liuyi中不是公共的; 无法从外部程序包中对其进行访问
    */

    十五、权限修饰符

     

    类及其组成可以用的修饰符

    l  类:

    •     默认,publicfinalabstract

    •     我们自己定义:public居多

    l  成员变量:

    •     四种权限修饰符均可,final,static

    •     我们自己定义:private居多

    l  构造方法:

    •     四种权限修饰符均可,其他不可

    •     我们自己定义:public 居多

    l  成员方法:

    •     四种权限修饰符均可,fianl,static,abstract

    •     我们自己定义:public居多

    /*
    	修饰符:
    		权限修饰符:private,默认的,protected,public
    		状态修饰符:static,final
    		抽象修饰符:abstract
    		
    	类:
    		权限修饰符:默认修饰符,public
    		状态修饰符:final
    		抽象修饰符:abstract
    		
    		用的最多的就是:public
    		
    	成员变量:
    		权限修饰符:private,默认的,protected,public
    		状态修饰符:static,final
    		
    		用的最多的就是:private
    		
    	构造方法:
    		权限修饰符:private,默认的,protected,public
    		
    		用的最多的就是:public
    		
    	成员方法:
    		权限修饰符:private,默认的,protected,public
    		状态修饰符:static,final
    		抽象修饰符:abstract
    		
    		用的最多的就是:public
    		
    	除此以外的组合规则:
    		成员变量:public static final
    		成员方法:public static 
    		          public abstract
    				  public final
    		
    */
    //此处不允许使用修饰符private
    //此处不允许使用修饰符protected
    //此处不允许使用修饰符static
    public class Demo {
    	//成员变量
    	private int x = 10;
    	int y = 20;
    	protected int z = 30;
    	public int a = 40;
    	public final int b = 50;
    	public static int c = 60;
    	public static final int d = 70;
    	//此处不允许使用修饰符abstract
    	//abstract int e = 80;
    	
    	//构造方法
    	private Demo(){}
    	
    	Demo(String name){}
    	
    	protected Demo(String name,int age) {}
    	
    	public Demo(String name,int age,String address) {}
    	
    	//此处不允许使用修饰符static
    	//public static Demo(){}
    	//此处不允许使用修饰符final
    	//public final Demo() {}
    	//此处不允许使用修饰符abstract
    	//public abstract Demo(){}
    	
    	//成员方法
    	//static void show() {}
    	//abstract void show();
    	//final void show(){}
    }

    十六、内部类

    把类定义在其他类的内部,这个类就被称为内部类。

    •    举例:在类A中定义了一个类B,类B就是内部类。

    内部类的访问特点:

    •     内部类可以直接访问外部类的成员,包括私有。

    •    外部类要访问内部类的成员,必须创建对象。

    /*
    	内部类概述:
    		把类定义在其他类的内部,这个类就被称为内部类。
    		举例:在类A中定义了一个类B,类B就是内部类。
    	
    	内部的访问特点:
    		A:内部类可以直接访问外部类的成员,包括私有。
    		B:外部类要访问内部类的成员,必须创建对象。
    	
    */
    class Outer {
    	private int num = 10;
    	
    	class Inner {
    		public void show() {
    			System.out.println(num);
    		}
    	}
    	
    	public void method() {
    		//找不到符号
    		//show();
    	
    		Inner i = new Inner();
    		i.show();
    	}
    	
    }
    
    class InnerClassDemo {
    	public static void main(String[] args) {
    	
    	}
    }

     

    内部类位置

    按照内部类在类中定义的位置不同,可以分为如下两种格式:

    •    成员位置(成员内部类)

    •    局部位置(局部内部类)

    /*
    	内部类位置
    		成员位置:在成员位置定义的类,被称为成员内部类。	
    		局部位置:在局部位置定义的类,被称为局部内部类。
    		
    		
    	成员位置:在成员位置定义的类,被称为成员内部类。	
    		
    */
    class Outer {
    	private int num = 10;
    
    	//成员位置
    	/*
    	class Inner {
    		
    	}
    	*/
    	
    
    	public void method() {
    		//局部位置
    		class Inner {
    		
    		}
    	}
    }
    
    class InnerClassDemo2 {
    	public static void main(String[] args) {
    		
    	}
    }

     

    成员内部类

    l  外界如何创建对象

    l  外部类名.内部类名对象名= 外部类对象.内部类对象;

    /*
    	成员内部类:
    		如何直接访问内部类的成员。
    		外部类名.内部类名 对象名 = 外部类对象.内部类对象;
    */
    class Outer {
    	private int num = 10;
    	
    	class Inner {
    		public void show() {
    			System.out.println(num);
    		}
    	}
    }
    
    class InnerClassDemo3 {
    	public static void main(String[] args) {
    		//需求:我要访问Inner类的show()方法
    		//Inner i = new Inner();
    		//i.show();
    		
    		//格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
    		Outer.Inner oi = new Outer().new Inner();
    		oi.show();
    	}
    }

     

    成员内部类

    刚才我们讲解过了,成员内部类的使用,但是一般来说,在实际开发中是不会这样使用的。因为一般内部类就是不让外界直接访问的。

    •    举例讲解这个问题:BodyHeart,电脑和CPU

    成员内部的常见修饰符

    •     private 为了保证数据的安全性

    •     static 为了让数据访问更方便

    •   被静态修饰的成员内部类只能访问外部类的静态成员

    •   内部类被静态修饰后的方法

    •   静态方法    

    •   非静态方法

    /*
    	成员内部类的修饰符:
    		private 为了保证数据的安全性
    		static 为了方便访问数据
    			注意:静态内部类访问的外部类数据必须用静态修饰。
    	
    	案例:我有一个人(人有身体,身体内有心脏。)
    		
    		class Body {
    			private class Heart {
    				public void operator() {
    					System.out.println("心脏搭桥");
    				}
    			}
    			
    			public void method() {
    				if(如果你是外科医生) {
    					Heart h = new Heart();
    					h.operator();
    				}
    			}
    		}
    		
    		按照我们刚才的讲解,来使用一下
    		Body.Heart bh = new Body().new Heart();
    		bh.operator();
    		//加了private后,就不能被访问了,那么,怎么玩呢?
    		Body b =  new Body();
    		b.method();
    */
    class Outer {
    	private int num = 10;
    	private static int num2 = 100;
    	
    	//内部类用静态修饰是因为内部类可以看出是外部类的成员
    	public static class Inner {
    		public void show() {
    			//System.out.println(num);
    			System.out.println(num2);
    		}
    
    		public static void show2() {
    			//System.out.println(num);
    			System.out.println(num2);
    		}		
    	}
    }
    
    class InnerClassDemo4 {
    	public static void main(String[] args) {
    		//使用内部类
    		// 限定的新静态类
    		//Outer.Inner oi = new Outer().new Inner();
    		//oi.show();
    		//oi.show2();
    		
    		//成员内部类被静态修饰后的访问方式是:
    		//格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
    		Outer.Inner oi = new Outer.Inner();
    		oi.show();
    		oi.show2();
    		
    		//show2()的另一种调用方式
    		Outer.Inner.show2();
    	}
    }

     

    成员内部类面试题

    补齐程序(注意:内部类和外部类没有继承关系)

          class Outer {

             public int num = 10;

             class Inner {

                  public int num = 20;

                  public void show() {

                       int num = 30;

                       System.out.println(?);

                       System.out.println(??);

                       System.out.println(???);

                  }

             }

          }

    在控制分别输出:302010
    System.out.println(num);
    System.out.println(this.num);
    System.out.println(Outer.this.num);

     

    局部内部类

    可以直接访问外部类的成员

    可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能

    局部内部类访问局部变量的注意事项:

    •    必须被final修饰?

    •    为什么呢?

          •   因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下。

    /*
    	局部内部类
    		A:可以直接访问外部类的成员
    		B:在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
    	
    	面试题:
    		局部内部类访问局部变量的注意事项?
    		A:局部内部类访问局部变量必须用final修饰
    		B:为什么呢?
    			局部变量是随着方法的调用而调用,随着调用完毕而消失。
    			而堆内存的内容并不会立即消失。所以,我们加final修饰。
    			加入final修饰后,这个变量就成了常量。既然是常量。你消失了。
    			我在内存中存储的是数据20,所以,我还是有数据在使用。
    */
    class Outer {
    	private int num  = 10;
    	
    	public void method() {
    		//int num2 = 20;
    		//final int num2 = 20;
    		class Inner {
    			public void show() {
    				System.out.println(num);
    				//从内部类中访问本地变量num2; 需要被声明为最终类型
    				System.out.println(num2);//20
    			}
    		}
    		
    		//System.out.println(num2);
    		
    		Inner i = new Inner();
    		i.show();
    	}
    }
    
    class InnerClassDemo5 {
    	public static void main(String[] args) {
    		Outer o = new Outer();
    		o.method();
    	}
    }

     

    匿名内部类

    就是内部类的简化写法。

    前提:存在一个类或者接口

    •     这里的类可以是具体类也可以是抽象类。

    格式:

    new类名或者接口名(){重写方法;}

    本质:

    •    是一个继承了类或者实现了接口的子类匿名对象

    /*
    	匿名内部类
    		就是内部类的简化写法。
    
    	前提:存在一个类或者接口
    		这里的类可以是具体类也可以是抽象类。
    	
    	格式:
    		new 类名或者接口名(){
    			重写方法;
    		}
    		
    	本质是什么呢?
    		是一个继承了该类或者实现了该接口的子类匿名对象。
    */
    interface Inter {
    	public abstract void show();
    	public abstract void show2();
    }
    
    class Outer {
    	public void method() {
    		//一个方法的时候
    		/*
    		new Inter() {
    			public void show() {
    				System.out.println("show");
    			}
    		}.show();
    		*/
    		
    		//二个方法的时候
    		/*
    		new Inter() {
    			public void show() {
    				System.out.println("show");
    			}
    			
    			public void show2() {
    				System.out.println("show2");
    			}
    		}.show();
    		
    		new Inter() {
    			public void show() {
    				System.out.println("show");
    			}
    			
    			public void show2() {
    				System.out.println("show2");
    			}
    		}.show2();
    		*/
    		
    		//如果我是很多个方法,就很麻烦了
    		//那么,我们有没有改进的方案呢?
    		Inter i = new Inter() { //多态
    			public void show() {
    				System.out.println("show");
    			}
    			
    			public void show2() {
    				System.out.println("show2");
    			}
    		};
    		
    		i.show();
    		i.show2();
    	}
    }
    
    class InnerClassDemo6 {
    	public static void main(String[] args) {
    		Outer o = new Outer();
    		o.method();
    	}
    }

     

    匿名内部类案例

    写案例,并测试

    •    如何调用方法

    •    加入方法有多个,如何调用呢?

    •   方式1:每一种格式调用一个,太麻烦

    •   方式2:用类或者接口接收该子类对象,多态思想

     

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

    首先回顾我们曾经讲过的方法的形式参数是引用类型的情况,重点是接口的情况,我们知道这里需要一个子类对象。而匿名内部类就是一个子类匿名对象,所以,可以使用匿名内部类改进以前的做法。

    /*
    	匿名内部类在开发中的使用
    */
    interface Person {
    	public abstract void study();
    }
    
    class PersonDemo {
    	//接口名作为形式参数
    	//其实这里需要的不是接口,而是该接口的实现类的对象
    	public void method(Person p) {
    		p.study();
    	}
    }
    
    //实现类
    class Student implements Person {
    	public void study() {
    		System.out.println("好好学习,天天向上");
    	}
    }
    
    class InnerClassTest2 {
    	public static void main(String[] args) {
    		//测试
    		PersonDemo pd = new PersonDemo();
    		Person p = new Student();
    		pd.method(p);
    		System.out.println("--------------------");
    		
    		//匿名内部类在开发中的使用
    		//匿名内部类的本质是继承类或者实现了接口的子类匿名对象
    		pd.method(new Person(){
    			public void study() {
    				System.out.println("好好学习,天天向上");
    			}
    		});
    	}
    }

     

    匿名内部类面试题

    按照要求,补齐代码

         interface Inter { void show(); }

         class Outer { //补齐代码}

         class OuterDemo {

            public static void main(String[] args) {

               Outer.method().show();

            }

         }

    要求在控制台输出HelloWorld

    /*
    	匿名内部类面试题:
    		按照要求,补齐代码
    			interface Inter { void show(); }
    			class Outer { //补齐代码 }
    			class OuterDemo {
    				public static void main(String[] args) {
    					  Outer.method().show();
    				  }
    			}
    			要求在控制台输出”HelloWorld”
    */
    interface Inter { 
    	void show(); 
    	//public abstract
    }
    
    class Outer { 
    	//补齐代码
    	public static Inter method() {
    		//子类对象 -- 子类匿名对象
    		return new Inter() {
    			public void show() {
    				System.out.println("HelloWorld");
    			}
    		};
    	}
    }
    
    class OuterDemo {
    	public static void main(String[] args) {
    		Outer.method().show();
    		/*
    			1:Outer.method()可以看出method()应该是Outer中的一个静态方法。
    			2:Outer.method().show()可以看出method()方法的返回值是一个对象。
    				又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。
    		*/
    	}
    }

     

     

    展开全文
  • 本课程从零开始,以通俗易懂的方式讲解Java入门,手把手教你掌握每一个知识点,真正做到零基础入门学习,最适合初学者的教程! 课程内容包括: 1.面向对象简介 2.类的使用 3.this关键字 4.static关键字 5.方法重载 ...
  • Java什么叫面向对象

    千次阅读 多人点赞 2018-08-15 21:56:17
    什么是面向对象呢?这个问题真的是老生常谈,面试的时候经常问。到底什么是面向对象。。。我也不知道啊  看看百度一下的结果吧: 世界上有loli控,御姐控,女王控,所以有句话就叫万物皆可控面向对象也是一样,...

        什么是面向对象呢?这个问题真的是老生常谈,面试的时候经常问。到底什么是面向对象。。。我也不知道啊

        看看百度一下的结果吧:

    世界上有loli控,御姐控,女王控,所以有句话就叫万物皆可控
    面向对象也是一样,万物皆对象
    对象这个词你可以理解为一个总称,比如男人女人都叫人,蜡烛皮鞭高跟鞋都是道具,所以面向对象原本的意思是“都叫对象”,翻译过来不是原汁原味了而已因此,房子是对象、飞机是对象、猫是对象、树是对象、女朋友也是对象,并且对象具有唯一性:你家的房子是你家的,不是我家的,你在国外看到的美女不是我在国内看到的美女,你的身份证号和我的身份证号不一样
    那么一个房子里都有啥呢,有电脑,还有动作片光盘、战争片录像带、人体艺术杂志……现在这些“包含”在房子里的东西就不是东西了,而是叫做“属性”……
    对象、属性都有了,剩下就是方法了方法也很好理解啊,意思就是你想要得到某种结果,就需要用特定的方法比如你女朋友生气了要哄吧,不高兴了要逗她开心吧,“哄”和“逗”这些就是让女朋友死心塌地的方法……诸如此类,发现一个美眉要用“追”这个方法,和女朋友逛街上碰到美女要用“窥”这个方法,恋爱都要用“谈”这个方法……
    ——对象就是一个概念,把所有的事物都称作为“对象”(没有不是对象的东西)——属性就是该事物的组成部分,所有可以被包含的部分都称为“属性”(没有不含属性的对象)——方法就是达成目标的途径,所有可行性的行为都成为方法(没有不存在方法的对象和属性,但是可以选择使用哪些方法)

     

        我想大家很多时候都会看到上面的话吧,上面的是我百度过来的,下面要说的才是我说的:

        在一开始的时候,我也那样的回答,什么万物皆对象啊,什么都是对象什么的。但是真的理解吗,不理解,说白了只是记了下来。工作之后一段时间了,最近对面向对象有所感悟。

       说说感悟过程吧,在我用socket时候,说是绑定数据关系,我去看源码,一层一层翻,一层一层的debug,希望能有所明白,在找的时候说白了,我也不大明白我想找什么东西,最后找到了,最后的最后,竟然只是往socket对象里面填几个数据,socket对象里面的几个成员变量赋值。一下子我的世界就崩溃了,这都什么啊,最后的最后竟然给我看的是这个。我瞬间懵住了,三观被毁,只能思考重建了。

           没错就是这样的。我理解的面向对象的编程到现在就是:对象其实是一个由成员变量组合起来,用于临时存一些数据的一个东西,我们通过调用对象的方法去使用这些数据,这样做的目的一般来说有两个:一是修改了成员变量的值,二是返回一些数据,一般来说,方法都是单功能的,而方法里面的数据来源于三个地方:第一个地方是方法的形参,第二个就是对象的成员变量,第三个就是系统的一些数据,或者是静态的全局变量。通过这样的方法,就能完成一些操作。即使是不知道对象里面的源码,知道这个方法是做什么的就行。      就这么简单,这就是面向对象,其实理解这些话很容易,深刻理解却很难,其实就是两句话:1,对象是有一些数据的东西,,,2,调用对象使用或者修改这些数据,或者返回一个你需要的东西,往往方法都是单功能的便于修改和维护。

         源码中,很多方法都是由说明的,只要看懂说明调用就好了,没必要去死抠一些东西,如果想明白这个方法的用途,单扣这个方法就ok了。我想这也是面向对象的一个好处吧。当对每个类,每个方法都加了注解之后,阅读的人只需要知道简单的流程就可以,如果想单独去研究其中的某个部分,去研究他的类里面的具体方法就好了。

         我以前总以为,一些操作一些东西应该是很难的,调用很多东西怎么怎么样。其实现在有点想明白,其实就是包含数据的对象调用方法去修改对象本身或者其他的对象的数据,或者运用本身数据去运算得到一些数据而已。没有那么多的调用底层的东西,都是表象,本质是数据计算和简单存储数据。当然这样说有点牵强和不严谨。

          即使是java底层真的调用了一些c/c++的东西,那些C啊、C++啊也基本上是数据计算和数据存储,都是这样子的 这就是计算机编程的大的框架,最后的最后归结到高低电平0,1和半导体的应用,,,除了高低电平0,1和半导体之类的硬件的东西,整个编程体系无非就是数据计算和找个地方把数据存起来,以便用于再计算。这只是我一家之言,反驳就不用了,不严谨很牵强,仅供参考。

          但是声明一点,java的编程,很多人只是知道new出来对象,创造对象,却不知道垃圾回收。虽然java提供了很好的垃圾回收机制,但是就空间复杂度而言,还是要控制的,知道每个变量什么时候活的状态不能被回收,什么时候死的状态可以被回收,对于每一个开发都很重要,要在程序到达一个阶段,一个结束去思考一下,哪些变量可以回收了,哪些不能回收了,对不需要但是还在引用的变量进行处理。这样节约了空间。不关注这些敲出来的代码质量很渣,没法看啊。虽然不需要像C那样需要析构函数,但java的垃圾回收还是很重要的。

            That‘s all,thanks!

           

    展开全文
  • java面向对象

    万次阅读 多人点赞 2018-08-21 16:51:59
    包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。 1、面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是...

    本文内容是根据毕晓东老师的视频教程总结而得。包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。

    1、面向对象

    • 面向对象是相对面向过程而言
    • 面向对象和面向过程都是一种思想
    • 面向过程强调的是功能、行为
    • 面向对象:将功能封装进对象,强调具备了功能的对象
    • 面向对象是基于面向过程的

    面向过程例子:

    把大象放进冰箱里分为以下步骤:把冰箱门打开;把大象放进去;关上冰箱门(强调过程和过程中所涉及的行为(强调行为、动作、过程))。

    用面向对象思想考虑:无论是打开冰箱,放进大象,关闭冰箱,所有操作都是操作冰箱这个对象,所以只需要将所有功能都定义在冰箱这个对象上,冰箱上就有打开、存储、关闭得所有功能 。      

    由上可知,面向对象是一种思想,能让复杂问题简单化,程序员不需要了解具体的实现过程,只需要指挥对象去实现功能。例,面试官面试面试者就是面向对象的体现,面试官需要找具有编程功能的对象(面试者),而我就是一个具有编程功能的对象,面试完后,让面试者去编程,面试者就去实现编程功能。

    面向过程和面向对象图示:

    2、类与对象的关系

    面向对象三大基本特征:封装、继承、多态。而面向对象的过程就是找对象、建立对象、使用对象、维护对象的关系的过程。

    2.1类和对象的关系:

    类:是对现实生活中事物的描述。

    对象:就是这类事物,实实在在存在的个体。

    如现实生活中的对象:张三、李四。想要描述对象张三和李四,就需要提取对象中的共性内容。即对具体对象的共性的抽取。在描述时,这些对象的共性有:姓名、性别、年龄、学习java功能。而每个学员又有自己独有的姓名、性别、年龄、学习方式。

    在java中,描述是用类的方式实现,而类是通过new操作符所产生的实体来实现,而这个实体在堆内存中再映射到java中去。简单的说,描述就是class定义的类,具体对象就是对应java在堆内存中用new建立的实体

    描述事物其实就是在描述事物的属性和行为(方法),属性对应的是类中的变量,行为对应的是类中的函数(方法)。其实定义类,就是在描述事物,就是在定义属性和行为,属性和行为共同成为类中的成员(成员变量和成员方法)。

    示例:描述汽车。

    package com.vnb.javabase;
    public class Car {
       //描述颜色
       String color="red";
       //描述轮胎数
       int num = 4;
       //运行行为
       void run(){
          System.out.println("color:"+color+";  轮胎数:"+num);
       }
    }
    
    class CarDemo{
       public static void main(String[] args) {
          //生产汽车。在java中通过new操作符来完成,其实就是在堆内存中产生一个实体
          //car引用型变量(句柄),car就是一个类类型变量。类类型变量指向对象(该类产生的实体)
          //堆内存中有默认初始化值(color=null);而"red"称为显示初始化值;
          Car car = new Car();
    
          //需求:将已有车的颜色改为蓝色,指挥该对象做使用。在java中指挥方式是,对象.对象成员
          car.color="blue";
    
          //让车行驶起来
          car.run();
       }
    }

    生产汽车类在内存中的图示及解析:

    如上图,首先在栈内存中有一个Car c;然后在堆内存中new Car(),默认初始化color为null,num为0;再将color设置显示初始化值为”red”,将num设置显示初始化值为4;将new Car()产生的地址值0x0099赋给栈内存,再由栈指向堆中的0x0099的地址。

    生产两辆车(建立多个对象)在内存中的图示及解析:

    如上图所示,建立多个对象的过程:首先在栈中初始化c,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color赋值为”red”,将num赋值为4。将new Car()产生的地址值0x0099赋给栈内存,再由栈指向堆中的0x0099的地址。

    在栈中初始化C1,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color赋值为”red”,将num赋值为4。将new Car()产生的地址值0x0045赋给栈内存,再由栈指向堆中的0x0045的地址。

    此处产生的是两个对象对应的两个地址值。

    多个引用指向同一个对象的图示及解析:

    如上图所示,首先在栈中初始化c,通过new Car()方式在堆中建立Car对象,初始化color为null,num为0,再将color设置显示初始化值为”red”,将num设置显示初始化值为4。将new Car()产生的地址值0x0078赋给c的栈内存,再由栈指向堆中的0x0078的地址。再将堆中num的值改为5(因为成员变量在堆内存中);

    建立第二个引用时,首先在栈中初始化c1,然后将c的地址值0x0078赋给c1,并且c1指向堆中的0x0078所引用的对象,再将堆中的对象的color改为green。由此,无论在c还是c1引用上修改值后,指向的都是堆内存中的对象地址值0x0078。所以打印出的值为:5,green。

    2.2成员变量和局部变量的区别:

    作用范围:成员变量作用于整个类中。局部变量作用于方法中或者语句中。

    在内存中的位置:成员变量在堆内存中,因为对象的存在,才在内存中存在;局部变量存在栈内存中。

    2.3匿名对象:

    匿名对象是对象的简化形式。有两种使用情况:当对对象方法仅进行一次调用时;匿名对象可以作为实际参数进行传递

    例如:

    Car c = new Car();

    c.num = 5;

    可简化为:new Car().num = 5;

                      new Car().color = “red”;

                      new Car().run();

    匿名对象和非匿名对象在内存中存储方式区别:

    如上图,匿名对象和非匿名对象区别(注:匿名对象在栈中无数据)。使用匿名对象时,当new Car().num = 5;执行完后创建的对象就成为垃圾;new Car().color = “red”执行完后,此对象也会成为垃圾,因此匿名对象调用属性是没有意义的。但是,new Car().run()有意义。

    综上所述,匿名对象调用属性无意义,调用方法有意义

    匿名对象的两种使用方式:

    • 匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样比较简化;但是,如果对一个对象进行多个成员调用,必须给这个对象起名字。例如:

    Car c =  new Car();

    c.run();

    c.num=4;

    new Car().run();

    • 匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。例如:

    需求:汽车修配厂,对汽车进行改装,将来的车都改成黑色且只有三个轮胎。

    main(){

     Car c = new Car();

     show(c);

    }

    public static void show(Car c){

    c.num = 3;

    c.color = “black”;

    c.run();

    }

    上例的内存图示及解析:

    执行main方法后,首先会再栈内存中有一个Car c的引用,然后会在堆内存中new一个Car对象,生成对应的地址值0x0034;当执行show()方法时,会在show方法中再建立一个引用c(注意连个引用不同),再将main方法中c的地址值通过匿名类参数形式传递给show()方法,因此两个引用指向的是同一个地址0x0034。匿名对象传入show()方法时,当show方法执行完后,栈中show()指向的内存空间将会成为垃圾,其对应指向的对象地址和对象也成为垃圾。

    所以就有了使用java程序写缓存机制:用java定义空间存储缓存型的数据,需要的时候就用,不需要的时候就不用。此时需保证其的生命周期,不指定则虚拟机会直接回收,而虚拟机回收垃圾是不定时的,且有些对象不会回收。所以写缓存的时候,在释放对象空间时,必须考虑对象的强引用、弱引用、软引用、虚引用。

    3、封装

    封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。(在使用对象时,没有必要知道对象内容是如何完成对应功能的,我们只需要指挥对象去执行即可)。

    封装的好处:将变化隔离(内部功能细节变化不影响使用);便于使用(不用了解内部的具体实现);提高重用性;提高安全性(只对外暴露一些简单的内容供使用)。

    封装的原则:将不需要对外提供的内容都隐藏起来;把属性都隐藏起来,只提供公共方法对其访问。

    函数本身就是java代码中的最简单的封装体。也是封装体,类中方法不一定全部暴露出去,可通过权限修饰符进行隐藏。也是封装体,如框架,拿来即用。

    函数封装体:

    封装具体示例:

    如上图,当p.age=-20时,就会出现安全隐患,人的年龄不可能为负数,所以可以使用修饰符进行控制权限。使用private int age;私有化后,类以外即使建立了对象也不能再直接访问该属性。但是属性或方法一旦私有了,就需要对外提供访问方式(即提供getter和setter方法等操作)。因此需要给这个人的年龄提供对外访问方式。

    之所以对外提供访问方式,是因为可以在这种访问方式中加入逻辑判断等语句,对访问的数据进行操作。提高代码的健壮性。如下例所示:

     在设置人的年龄时,set方法中需要满足条件,才可继续执行,否则提示非法。

    该示例在内存中表示,如下图:

    执行main()方法后,在栈内存中产生了一个p的引用,然后在堆内存中建立Person对象,并分配内存空间0x0012,并将这个地址值赋给p引用,然后由p再指向Person对象中的这个地址0x0012。new Person对象,执行到private int age时,在堆内存(成员变量在堆内存中分配空间)中产生了一个age,并有了默认初始化值0。当p引用设置年龄为40时(p.setAge(40)),会通过setAge()方法中的this.age = a(this即代表p引用)将堆中的age值改为40。当执行p.speak()时,拿到成员变量age的值40并输出(成员变量age由局部(setAge())接收进来并改变其值后,其他方法(speak())也可以用(拿到的还是setAge()执行后的40))。

    私有仅仅是封装的一种表现形式,只要权限在别人访问不到的范围都是封装。

     

    4、构造函数

    构造函数特点:

    • 函数名与类名相同
    • 不用定义返回值类型
    • 不可以写return语句

    作用:给对象进行初始化。

    构造函数示例:

    构造函数对象一建立就会调用与之对应的构造函数,可用于给对象进行初始化。当一个类中没有定义构造函数时,系统会默认给该类加入一个空参数的构造函数,当自己定义了构造函数后,默认的空构造函数就不存在了。

    注意:默认构造函数的特点;多个构造函数是以重载的形式存在的

    多个构造函数以重载形式存在的示例:

    构造代码块:

    在构造代码块中,对象一建立就立即运行,而且优先于构造函数执行。构造代码块中定义的是不同对象具有共性的初始化内容

    构造代码块和构造函数的区别:构造代码块是给所有对象进行统一初始化;而构造函数是给对应的对象初始化。

    5、this关键字

    5.1this关键字的基本阐述

    如上图,发现Person(String a)构造函数中,a变量是无意义的,不能见名知意。因此将a改成name表示姓名,即局部变量和成员变量名称相同。但如下图,发现执行结果name并没有赋值成功:

     

    因此如何区分局部和成员变量?即使用this关键字。

    如上图,this关键字看上去,是用于区分局部变量和成员变量同名情况。其实,this代表本类的对象。

    那么this到底代表哪一个对象?如下图:

    this代表它所在函数所属对象的引用。简单的说,哪个对象在调用this所在的函数,this就代表哪个对象。例如,当执行到Person p = new Person("lisi");时,建立Person对象,执行到Person对象的构造函数Person(String name)后,此时的this代表的是p引用。而当代码执行到Person p1 = new Person("zhangsan");时,执行到Person对象的构造函数Person(String name)后,this代表的是p1引用。

    5.2this关键字的应用:

    需求:给人定义一个判断是否是同龄人的功能。

    如上图,this和p1的地址值是指向同一个对象。当执行到boolean b = p1.compare(p2);时,会调用到compare(Person p)方法,此时,p2会以参数形式传给compare()方法,而由于是由p1调用的compare()方法,所以p1引用即当前对象this引用。即his和p1的地址值是指向同一个对象。

    this的应用:当定义类中方法时,该方法内部要用到调用该方法的对象时,这时用this表示这个对象。但凡本类功能内部使用到了本类对象,都用this表示。

    5.3this关键字在构造函数中的应用:构造函数间调用,只能使用this进行互相调用,this函数不能用在一般函数间

    this(name);对Person对象进行姓名初始化,this代表本对象。this.name = name是将值传递过去。

    this语句(不是this关键字)只能定义在构造函数的第一行。如下图:

    初始化动作要先执行,如先执行this(name)必须在this.name=name前才行。

    6、static关键字

    6.1概述

    static关键字:用于修饰成员(成员变量和成员函数)

    被修饰后的成员具备以下特点:

    • 随着类的加载而加载(类一加载到内存中时,静态static就已经加载到内存空间(方法区)中,反之随着类的消失而消失,说明它的生命周期最长)
    • 优先于对象存在(静态是先存在的,对象是后存在的)
    • 被所有对象所共享
    • 可以直接被类名调用

    使用注意:

    • 静态方法只能访问静态成员
    • 静态方法中不可以写this,super关键字
    • 主函数是静态的

    6.2为什么要使用静态static关键字?

    如以上两图所示,创建了两个对象引用P和P1,如果只在中国范围内,而不使用静态关键字static,则需要在堆内存中开辟两个country=”cn”空间(多个空间存在共同数据),那么对象创建得越多占用的内存就越多,如下图,将country=”cn”定义成静态的,则只需要在方法区中定义一次即可(static String country = “CN”),如下图:

    方法区(共享区、数据区):存放共享数据,方法及方法体等内容。

    当成员(包括成员变量和方法)被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。格式:类名.成员。

    静态static优先于对象的存在,即静态会随着类的加载而加载,消失而消失(说明生命周期最长)。静态的成员变量也叫类变量,非静态的成员变量也叫实例变量。

    6.3为什么不把所有的成员都定义为静态static?

    要区分什么数据是对象特有的,什么数据是多个对象共享的,这样才符合生活中的描述;对象在被用完后会被回收,但是静态static的数据生命周期特别长,用完后还会一直存在,因此会存在垃圾

    6.4实例变量和类变量的区别

    • 存放位置:类变量(静态成员变量)随着类的加载而存在于方法区中;实例变量随着对象的建立而存在于堆内存中
    • 生命周期:类变量生命周期最长,随着类的消失而消失;实例变量生命周期随着对象的消失而消失。

    6.5静态的使用注意事项

    • 静态方法只能访问静态成员(成员变量和方法),非静态方法既可以访问静态成员也可以访问非静态成员

    • 静态方法中不可以定义this,super关键字。因为静态优先于对象存在

    如下图,this.name不可用,this代表对象,而静态方法优先于对象创建,在执行静态方法show()时,对象Person还未创建,所以此时this 还未初始化过,所以不可用。

    6.6静态有利有弊

    利:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中存储一份;可以直接被类名调用。

    弊:生命周期过长(可能会有垃圾不能被回收);访问出现局限性(静态虽好,但只能访问静态)。

    6.7主函数中的静态

    主函数是静态的:主函数是一个特殊的函数,作为程序的入口,可以直接被JVM调用。

    主函数的定义:

    • public:代表着该函数访问权限是最大的
    • static:代表主函数随着类的加载就已经存在了
    • void:代表主函数没有具体的返回值返给虚拟机JVM
    • main:不是关键字,但是是一个特殊的单词,可以被JVM识别,且是主函数特有的。
    • String[] args: 函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组。

    主函数是固定格式的:JVM识别

    public static void main(int x){}也可以写(主函数被重载),但是JVM会优先从public static void main(String[] args){}开始执行。JVM在调用主函数时,传入的是 new String[0],作用:可以往参数里传入数据,在主函数里就可以拿到参数。

    传递参数给主函数并由主函数获取:

    • 使用命令传: java 类名 参数

    • 通过另一个类的主函数传值,然后由测试类主函数进行获取:

    6.8什么时候使用静态?

    要从两个方面下手:因为静态修饰的内容包括成员变量和成员函数。

    什么时候定义静态变量(类变量)?

    当对象中出现共享数据时,该数据需要被静态所修饰;而对象中的特有数据要定义成非静态存在堆内存中。

    6.9什么时候定义静态函数?

    当功能内部没有访问到非静态数据(对象的特有数据(非静态成员变量))时,该功能可以定义成静态的。

    例:对象是为了封装数据的,但是下例中对象里的特有数据name并没有使用到,所以show()方法可以使用静态。

    class Person{
    String name;
    public static void show(){
      System.out.println(“haha”);
    
    }
    
    public static void main(String[] args){
        Person.show();
       }
    }

    但是下例中有使用到对象中的非静态成员name,所以show()方法绝对不能使用静态:

    class Person{
    String name;
    public static void show(){
      System.out.println(name+“haha”);//因为静态先于对象存在,在创建静态方法show时,对象Person还不存在,所以name也不存在
    }
    
    public static void main(String[] args){
     Person p = new Person();
     p.show();
    }
    }

    6.10静态的应用—工具类

    如上图,一个获取数组中最大值的类。可以发现,如果有很多类,都需要使用到获取数组最大值这个方法时,每个类都需要写一遍这些代码,因此可以将获取数组最大值的代码统一封装到一个静态工具类中,需要使用时调用即可。如下图所示:

    因此,静态工具类即将每一个应用程序中都有共性的功能,进行抽取,独立封装,以便复用。

    原始的ArrayTool.java类:

    package com.vnb.javabase;
    /**
     * 
     * Description:数组工具类
     * @author li.mf
     * @date 2018年8月15日
     *
     */
    public class ArrayTool {
    
    	/**
    	 * 获取数组最大值
    	 * @param arr
    	 * @return
    	 */
    	public int getMax(int[] arr){
    	
    		int max = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[max]<arr[i]){
    				max = i;
    			}
    		}
    		return arr[max];
    	}
    	
    	/**
    	 * 获取数组最小值
    	 * @param arr
    	 * @return
    	 */
    	public int getMin(int[] arr){
    		
    		int min = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[min]>arr[i]){
    				min = i;
    			}
    		}
    		return arr[min];
    	}
    	/**
    	 * 选择排序
    	 * @param arr
    	 */
    	public void selectSort(int[] arr){
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					swap(arr,i,j);
    				}
    			}
    		}
    		
    	}
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public void bubbleSort(int[] arr){
    		for (int i = 0; i < arr.length; i++) {
    			for (int j = 0; j < arr.length-i-1; j++) {
    				if(arr[j]>arr[j+1]){
    					swap(arr,j,j+1);
    				}
    			}
    		}
    		
    	}
    	
    	public void swap(int[] arr,int a,int b){
    		int temp = arr[a];
    		arr[a] = arr[b];
    		arr[b] = temp;
    	}
    	
    	public void printArray(int[] arr){
    		System.out.print("{");
    		for (int i = 0; i < arr.length; i++) {
    			if(i!=arr.length-1){
    				System.out.print(arr[i]+",");
    			}else{
    				System.out.print(arr[i]+"}");
    			}
    		}
    	}
    }
    

    数组工具类的测试类ArrayToolTest.java:

    package com.vnb.javabase;
    
    public class ArrayToolTest {
    
    	public static void main(String[] args) {
    
    		int[] arr = {1,2,6,8,3};
    		
    		ArrayTool arrayTool = new ArrayTool();
    		int max = arrayTool.getMax(arr);
    		System.out.println("最大值为:"+max);
    		
    		int min = arrayTool.getMin(arr);
    		System.out.println("最小值为:"+min);
    		
    		arrayTool.printArray(arr);
    		arrayTool.selectSort(arr);
    		arrayTool.printArray(arr);
    	}
    }
    

    由例可见,ArrayTool类都没有用到ArrayTool中特有的数据(非静态的成员变量),都是由用户传参的arr进去,所以可以写成static。虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。但是,对象是用于封装数据的,而ArrayTool对象并未封装特有数据;操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。所以,这时就可以考虑,让程序更严谨而不需要对象。可以将ArrayTool中的方法都定义成static的,直接通过类名调用即可。

    ArrayTool.java中所有方法写成静态,以便调用:

    package com.vnb.javabase;
    /**
     * 
     * Description:数组工具类
     * @author li.mf
     * @date 2018年8月15日
     *
     */
    public class ArrayTool {
    
    	/**
    	 * 获取数组最大值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMax(int[] arr){
    	
    		int max = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[max]<arr[i]){
    				max = i;
    			}
    		}
    		return arr[max];
    	}
    	
    	/**
    	 * 获取数组最小值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMin(int[] arr){
    		
    		int min = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[min]>arr[i]){
    				min = i;
    			}
    		}
    		return arr[min];
    	}
    	/**
    	 * 选择排序
    	 * @param arr
    	 */
    	public static void selectSort(int[] arr){
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					swap(arr,i,j);
    				}
    			}
    		}
    		
    	}
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public static void bubbleSort(int[] arr){
    		for (int i = 0; i < arr.length; i++) {
    			for (int j = 0; j < arr.length-i-1; j++) {
    				if(arr[j]>arr[j+1]){
    					swap(arr,j,j+1);
    				}
    			}
    		}
    		
    	}
    	
    	public static void swap(int[] arr,int a,int b){
    		int temp = arr[a];
    		arr[a] = arr[b];
    		arr[b] = temp;
    	}
    	
    	public static void printArray(int[] arr){
    		System.out.print("{");
    		for (int i = 0; i < arr.length; i++) {
    			if(i!=arr.length-1){
    				System.out.print(arr[i]+",");
    			}else{
    				System.out.print(arr[i]+"}");
    			}
    		}
    	}
    }
    

    ArrayToolTest.java

    package com.vnb.javabase;
    
    public class ArrayToolTest {
    
    	public static void main(String[] args) {
    
    		int[] arr = {1,2,6,8,3};
    		
    		int max = ArrayTool.getMax(arr);
    		System.out.println("最大值为:"+max);
    		
    		int min = ArrayTool.getMin(arr);
    		System.out.println("最小值为:"+min);
    		
    		ArrayTool.printArray(arr);
    		ArrayTool.selectSort(arr);
    		ArrayTool.printArray(arr);
    	}
    }
    

    将方法都写成静态后,可以方便与使用,但是仍然发现问题,即该类还是可以被其他程序建立对象的。所以为了更为严谨,强制该类不可以实例化以建立对象,所以可以考虑通过将构造函数私有化完成(私有构造)。

    package com.vnb.javabase;
    /**
     * 
     * Description:数组工具类
     * @author li.mf
     * @date 2018年8月15日
     *
     */
    public class ArrayTool {
    
    	private ArrayTool(){
    		
    	}
    	/**
    	 * 获取数组最大值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMax(int[] arr){
    	
    		int max = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[max]<arr[i]){
    				max = i;
    			}
    		}
    		return arr[max];
    	}
    	
    	/**
    	 * 获取数组最小值
    	 * @param arr
    	 * @return
    	 */
    	public static int getMin(int[] arr){
    		
    		int min = 0;
    		for (int i = 1; i < arr.length; i++) {
    			if(arr[min]>arr[i]){
    				min = i;
    			}
    		}
    		return arr[min];
    	}
    	/**
    	 * 选择排序
    	 * @param arr
    	 */
    	public static void selectSort(int[] arr){
    		for (int i = 0; i < arr.length-1; i++) {
    			for (int j = i+1; j < arr.length; j++) {
    				if(arr[i]>arr[j]){
    					swap(arr,i,j);
    				}
    			}
    		}
    		
    	}
    	/**
    	 * 冒泡排序
    	 * @param arr
    	 */
    	public static void bubbleSort(int[] arr){
    		for (int i = 0; i < arr.length; i++) {
    			for (int j = 0; j < arr.length-i-1; j++) {
    				if(arr[j]>arr[j+1]){
    					swap(arr,j,j+1);
    				}
    			}
    		}
    		
    	}
    	
    	private static void swap(int[] arr,int a,int b){
    		int temp = arr[a];
    		arr[a] = arr[b];
    		arr[b] = temp;
    	}
    	
    	public static void printArray(int[] arr){
    		System.out.print("{");
    		for (int i = 0; i < arr.length; i++) {
    			if(i!=arr.length-1){
    				System.out.print(arr[i]+",");
    			}else{
    				System.out.print(arr[i]+"}");
    			}
    		}
    	}
    }
    

    ArrayToolTest.java

    package com.vnb.javabase;
    
    public class ArrayToolTest {
    
    	public static void main(String[] args) {
    
    		int[] arr = {1,2,6,8,3};
    		
    		int max = ArrayTool.getMax(arr);
    		System.out.println("最大值为:"+max);
    		
    		int min = ArrayTool.getMin(arr);
    		System.out.println("最小值为:"+min);
    		
    		ArrayTool.printArray(arr);
    		ArrayTool.selectSort(arr);
    		ArrayTool.printArray(arr);
    	}
    }
    

     

    6.11帮助文档的制作(javadoc)

    如果从别的地方拿来的java文件且不在同一目录下,则执行时会报错:找不到文件。如下图,

    所以可以在dos命令行中设置classpath:set classpath-.i;c:\myclass 。-.i;先在当前目录下找,再C盘指定目录myclass下去找。

    /** */  里面写类的描述信息、开发人、开发时间、版本号等。java通过javadoc.exe程序来进行帮助文档的制作。通过命令可以将帮助文档存放到指定位置:javadoc –d myhelp –author –version ArrayTool.java。(如果无此文件夹,会自动创建)。

    要将一个文件写出帮助文档,需要将类修饰符写为public或者protected 。以供外部读取。

    ArrayTool.java帮助文档的查看:

    从索引页面开始看即可(index.html)。

    帮助文档示例:

    一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,如果类被public修饰,那么默认的构造函数也带public修饰,如果没有,默认的空构造也没有。注意:默认的构造函数不是自己写的空构造函数。

    6.12静态代码块

    格式:

    static{
         静态代码块的执行语句
    }

    示例:

    static{
         System.out.println(“asdf”);
    }

    静态代码块特点:静态代码块随着类的加载而执行,且执行一次(类加载完后,执行完了,就已经在内存中了),用于给类进行初始化,且优先于主函数执行。

    静态代码块和主函数执行,示例:

    打印顺序:b c a。

    如上图,想让show方法执行,必须先加载StaticCodeDemo类。

    如上图,该类类变量没有任何实体指向,所以StaticCode类不会进行加载,所以没有打印结果。

    如上图,执行顺序:a c d ; 注:如果想要执行特定的构造函数需要在调用的时候进行指定,如上例new StaticCode(4);指定调用的是StaticCode(int x){}构造方法,而不是空参数的构造方法。

    • a表示静态代码块给初始化;
    • b不会执行,因为还未创建过与之对应的对象(已经指定调用其他的构造方法);
    • c表示构造代码块给对象初始化
    • d表示构造函数给对应对象初始化

    {}表示构造代码块给对象初始化的,所以可以用this

    6.13对象初始化的过程

    对象建立过程:

    当类的主函数入口main()方法执行到Person p = new Person(“zhangsan”,20);时,会有一个p引用加载到栈中:

    1)new Person()时会将Person.class文件从硬盘中通过java的虚拟机JVM加载进内存;

    2)执行静态代码块

    3)并开辟了堆内存空间。(非静态成员变量,如属性:name、age)

    4)初始化动作,构造代码块

    执行结果null,0。

    所以会先有默认初始化null和0,再有显示初始化(成员属性的初始化),如下图:

    再进行构造代码块初始化(此代码中无),再构造函数初始化(赋值zhangsan,20)。

    总结:Person p = new Person(“zhangsan”,20);的执行过程?

    1. 因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中
    2. 执行该类中的static代码块,如果有的话,给Person.class类进行初始化
    3. 在堆内存中开辟空间,分配内存地址
    4. 在堆内存中建立对象的特有属性,并进行默认初始化(对象为null,整数为0)
    5. 对属性进行显示初始化(类中声明成员变量初始化值)
    6. 对对象进行构造代码块初始化
    7. 对对象进行对应的构造函数初始化
    8. 将内存地址值赋给栈内存中的p引用
    9. p引用再指向堆内存中的地址值

    6.14对象调用成员过程

    Person p = new Person(“zhangsan”,20);

    p.setName(“lisi”);

    1)在栈中有main主函数的p引用生成

    2)然后在方法区中初始化Person类的方法(showCountry()、setName()、speak())、方法体、静态变量

    3)在堆中开辟空间

    4)在默认初始化(对象为null,整数为0)

    5)再到堆中new Person(“Zhangsan”,20)

    6)再将地址值0x0023赋给栈,再从栈中指向堆中的地址

    7)setName()在栈中开辟空间,非静态的只要一调用this就会有值(指向),this的引用用于给对象赋值(哪个对象调用就代表哪个对象,这里this代表p),即this为0x0023,即this指向堆中的0x0023,因此 setName中this.name = name一执行时,局部变量name的lisi赋值给了堆内存中的this.name。

    调用过程的内存图示:

    当再创建一个对象p1时,在内存中的调用过程,如下:

    当再创建一个对象p1后,会重新在栈内存中初始化p1引用,然后在堆内存中重新new 一个Person对象,设置默认初始化值,然后设置显示初始化值name为ahah,age为90,并开辟空间设置地址值,并将地址值赋给p1,当setName(“qq”)时,会在栈中开辟一块空间,通过this.name = name将堆内存中的name值设置为qq。

    如上图,静态方法showCountry()方法一被调用,在开辟完空间后,没有找到this关键字,所以showCountry()方法就会所属于Person类,不会再进堆内存。调用showCountry()方法时也会用“类名.showCountry()”执行里面的静态方法。

    成员只有被调用才能用,静态的本类省略了类名.method。非静态对象省略this.方法; 静态省略类名.静态方法。

    7、单例设计模式

    7.1概念

    设计模式: 解决某一类问题最行之有效的方法。java中共有23种设计模式。

    单例模式:解决一个类在内存中只存在一个对象(不能new多个实例)

    想要保证对象唯一:

    1. 为了避免其他程序过多建立该类对象,先要控制禁止其他程序建立该类对象
    2. 为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象
    3. 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式

    代码实现:

    1. 将构造函数私有化(以控制其他程序建立该类对象)
    2. 在类中创建一个本类对象
    3. 提供一个方法可以获取到该对象(方便其他程序对自定义对象的访问)

    7.2饿汉式单例模式

    package com.vnb.javabase;
    /**
     * 
     * Description:单例模式
     * @author li.mf
     * @date 2018年8月16日
     *
     */
    public class Singleton {
    	
    	private static Singleton instance = new Singleton();
    	
    	private Singleton(){
    		
    	}
    	
    	public static Singleton getInstance(){
    		
    		return instance;
    	}
    }
    
    class SingleDemo{
    	
    	public static void main(String[] args) {
    		Singleton ss = Singleton.getInstance();
    	}
    }
    

    内存中实现过程:

    再获取一次单例,发现获取的实例时一致的。如下图:

    测试是否获取的是同一个实例:

    class SingleDemo{
       public static void main(String[] args) {
          Singleton s1 = Singleton.getInstance();
          Singleton s2 = Singleton.getInstance();
          s1.setNum(20);
          System.out.println("num值为"+s2.getNum());
       }
    }

    结果发现,s2获取的值是s1 set进的值。

    单例模式,对于事物该怎么描述,还怎么描述,当需要将该事物的对象保证在内存中唯一时,就将以上者三步加上即可。

    7.3懒汉式单例模式

    package com.vnb.javabase;
    public class Single {
    	private static Single instance = null;
    	private Single(){	
    	}
    	public static Single getInstance(){
    		if(instance == null){
    			instance = new Single();
    		}
    		return instance;
    	}
    }
    

    对象在被调用时,才进行初始化,就叫做对象的延时加载,也称为懒汉式。

    如下图,Single类进内存时,对象还没有存在,而是只有调用了getInstance方法时,才建立对象。

    懒汉式单例模式在内存中表示:

    开发一般使用饿汉式,因为其简单且安全,且懒汉式当有多个人来调用这个方法时会出现问题。当A执行到if(s==null) 后挂掉了,突然去执行其他程序了,此时其他程序B来判断后new了一个对象,之后A又继续执行了,但是A并不知道B已经创建了对象,所以B又new了一个对象,这样就new了多个对象不唯一了。

    为解决上述问题:可以在方法上加同步锁synchronized

    package com.vnb.javabase;
    public class Single {
    	private static Single instance = null;
    	private Single(){	
    	}
    	public static synchronized Single getInstance(){
    		if(instance == null){
    			instance = new Single();
    		}
    		return instance;
    	}
    }
    

    但是加上同步锁后,每次进getInstance()方法后都要执行同步锁,程序的效率会变低。

    为解决低效率问题:可以使用双重判断形式(多线程时进行讲解),使用同步代码块。如下图。但是此方法代码会较多。所以定义单例时建立使用饿汉式。

    package com.vnb.javabase;
    public class Single {
    	private static Single instance = null;
    	private Single(){	
    	}
    	public static Single getInstance(){
    		if(instance == null){
    			synchronized (Single.class) {
    				if(instance == null){
    					instance = new Single();
    				}
    			}
    		}
    		return instance;
    	}
    }
    

    8、继承

    本节主要内容:继承的概述;继承的特点;super关键字;函数覆盖;子类的实例化过程;final关键字。

    8.1继承的概述

    有以下两个类:

    将学生和工人两个类的共性描述提取出来,单独进行描述,只要让学生和工人与单独描述的这个类有关系,就可以了。

     

    继承的好处:提高了代码的复用性;继承让类与类之间产生了关系,有了这个关系,才有了多态的特性。

    注意:千万不能为了获取其他类的功能,简化代码而继承。必须是类与类之间有所属关系才可以继承。所属关系即 is a。java语言中,只支持单继承,不支持多继承,因为多继承容易带来安全隐患(接口与接口之间可以多继承(因为接口之间没有方法体,方法之间不会冲突))。

    如上图,继承多个类,当多个父类中定义了相同功能,当功能内容不同时,子类对象不知道运行哪个功能,就会出现问题。但是java保留另一种体现形式来完成这种表示,叫接口多实现。

    java支持多层继承。也就是一个继承体系。如下图所示:

    C类也可以使用到A类中的功能。

    如何使用一个继承体系中的功能呢?

    想要使用体系,先查阅体系中父类的描述,因为父类中定义的是该体系中的共性功能,通过了解共性功能,就可以知道该体系的基本功能。也可以基本使用了。在具体调用时,为什么要创建最子类的对象?一是因为有可能父类不能创建对象;二是创建子类对象可以使用更多的功能,包括父类的也包括特有的。简单的一句话:查阅父类功能,创建子类对象使用功能。

    8.2类与类之间关系—聚集关系(组合关系)

    事物之间不一定是继承关系,也有聚集关系 has a,简单的说就是谁里面有谁。

    聚合:球员和球队(球队中有球员)

    组合:事物之间的联系比聚合关系更高,比如,心脏和人;手和人,心脏和手必不可少。

    8.3子父类中变量的特点

    以上当父类子类成员变量相同时,打印的是4,因为子类中的num 即为this.num。要打印父类的变量,使用super.num即可,打印子类的变量直接使用num即可。

    new 子类class文件时,会先加载父类的class文件

    变量:如果子类中出现非私有的同名变量时,子类要访问本类中的变量,用this;子类要访问父类中的同名变量,用super。super的使用和this的使用几乎一致,this代表本类对象的引用;super代表父类对象的引用。

    如上图,num前省略super和this打印结果都是4,原因:子类继承父类,子类也能拿到父类的num 4(相当于子类里有自己的num),所以用子类自己的this能拿到num=4;super是指父类引用,this是指本类引用,由于现在没有父类对象只有子类对象(Zi z = new Zi()),所以现在super和this引用指向的是同一个对象(Zi子类对象)即new Zi(),所以只有一个num=4,即同一个子类对象的num(这就是面向对象的多态即父类引用指向子类对象List list = new ArrayList())。

    8.4子父类中函数的特点—覆盖(重写)

    如上图,若子父类中的方法一致,子类运行就会覆盖了父类中的方法。子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,如同父类的函数被覆盖一样,这种情况是函数的另一个特性,重写(覆盖)。

    当子类继承了父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但是功能的内容却和父类却不一致,这时,就没有必要定义新功能,而是使用覆盖特性,保留父类功能定义,并重新父类内容。这就叫重写,也叫做覆盖。如下图:

    用重写特性提高程序扩展性:

    new的时候只要new NewTel即可,而不需要修改Tel类的任何内容,但是重写时会有代码重复以实现父类原有的功能,所以只要使用super调用原有内容即可。如下图:

    覆盖:子类覆盖父类,必须保证子类权限要大于或等于父类权限,才可以覆盖,且父类不能为private修饰(如果是private修饰,子类都不知道父类有此方法存在),否则编译失败;静态只能覆盖静态(虽然此做法无意义);

    重载和重写的区别:

    • 重载只看同名函数的参数列表;
    • 重写是子父类方法要一模一样包括返回值类型。

    如上图,是不允许存在的。因此重写要求子父类中的方法名和返回值类型必须相同。

    8.5子父类中构造函数的特点—子类实例化过程

    子父类构造函数不能覆盖,因为覆盖必须函数名和返回值等都一模一样,但是构造函数的名字必须和类名一致;子类的构造函数有一句隐示的语句super();如下图:

    在对子类对象进行初始化时,父类构造函数也会运行,是因为子类的构造函数默认第一行有一条隐式的语句super(),super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。如下图所示:

    如下图,当父类中没有空参数的构造时,系统会报错,所以必须手动指定子类引用父类的哪个非空的构造函数。

    为什么子类一定要访问父类中的构造函数?见下图:

    总结:(如上图)因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在对象初始化时,要先访问一下父类中的构造函数,如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式进行指定。如上图,在建立子类对象后后去查看父类有没有改变num的值,这里父类已经将num值设置为60,所以子类的num也对应改了,最后打印的z.num即父类中设置的60。

    应用:

    父类已经给name赋过值了,子类继承父类后,直接调用(super(name))父类的方法即可获取到父类中设置的值。

    总结:父类已经定义过的方法属性,子类想要使用,直接通过super即可拿到

    如下图,super()语句必须写在子类构造函数的第一行,因为需要先把父类的数据初始化完成后,再初始化自己的。

    当子类构造函数中写了this()方法后,就不能再用super()了,this和super不能同时存在。此时zi(int x)中的this()会访问本类中的zi()空构造,而zi()构造函数中默认有super()就能调用到父类中的属性和方法。

    结论(子类的实例化过程):子类中所有的构造函数,默认都会访问父类中的空参数的构造函数,因为子类每一个构造函数内的第一行都有一句隐式的super(),当父类中没有空参数的构造函数时,子类必须手动通过super语句或者this语句形式来指定要访问的父类中的构造函数。子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少要有一个构造函数会访问父类中的构造函数(没写也会有默认的)。

    this和super为什么不能同时存在?因为this和super都必须存在第一行。

    为什么this和super都必须存在第一行?因为初始化动作必须先做。

    8.6 final关键字

    final关键字:

    • 作为一个修饰符,可以修饰类、函数、变量。
    • 被final修饰的类不可以被继承(继承的弊端,打破了封装性,可以复写掉父类的东西,很多基类就会很不安全,可能会被修改),为了避免被继承,被子类复写功能,在类上写final即可(叫做最终类,最终类是不可以被继承的)。
    • 被final修饰的方法,不可以被复写。

    1. 被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量,也可以修饰局部变量。如下图:

    只要在内存中,x和y的值都是固定的,不可以更改。

    当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,方便阅读,而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有的字母都大写,如果有多个单词组成,单词间通过下划线连接。

    2.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。

    8.7抽象类

    如上图,发现两个方法都一致,所以需要抽取相同功能。但发现虽然两者都在学习,但是学习内容却不一样。解决:可以进行向上抽取,只抽取功能定义,不抽取功能主体(即抽象类)。对于这种方法,必须使用abstract进行修饰。抽象方法必须定义在抽象类中。如下图:

    抽象:即看不懂的事物。

    抽象类的特点:

    1. 抽象方法一定在抽象类中
    2. 抽象方法和抽象类都必须被abstract关键字修饰
    3. 抽象类不可以用new创建对象,因为调用抽象方法没意义(里面的抽象方法连方法体都没有)
    4. 抽象类中的抽象方法要想被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用,如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

    抽象类和一般类没有太大的不同。只是要注意该如何描述事物就如何描述事物,只不过该事物中出现了一些看不懂的东西。这些不确定的部分也是该事物的功能,需要明确出来,但是无法定义主体。而通过抽象方法来表示。

    • 抽象类比一般类多了抽象方法。就是在类中可以定义抽象方法,也可以不定义抽象方法。
    • 抽象类不可以实例化。
    • 抽象类也可以不定义抽象方法,这样只是为了不让该类创建对象。

    抽象类练习:

    假如我们在开发一个系统时需要对员工进行建模,员工包含3个属性:姓名、工号、工资。经理也是员工,除了含有员工的属性外,另外还有一个奖金属性,请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。

    分析:

    员工类:name、id、pay

    经理类:继承了员工并有自己特有的奖金属性bonus

    会有一个抽象员工类,员工属于这一类,经理也属于这一类

    8.8模板方法设计模式:

    需求:获取一段程序运行的时间

    原理:获取程序开始和结束的时间并相减即可。获取本机系统时间:System.currentTimeMillis()获取当前时间的毫秒值,返回当前时间减去1970年1月1日0点0分0秒之间的时间差。

    问题:想获取另一段代码的运行时间。解决:直接子类复写掉getTime方法

    但是发现代码重复性很高。

    解决:可以将需要运行代码提取出来形成另一个方法,子类复写时,只需要复写提取出来的代码即可。如下图:

    但是却发现提取出来的需要运行的代码是不确定的,解决:只要将方法写成抽象方法并由子类继承后进行具体实现即可,且getTime方法不可复写所以使用final进行修饰。如下图:

    当代码完成优化后,就可以解决这类问题,这种方式叫做模板方法设计模式。

    什么是模板方法?

    在定义功能时,功能的一部分是确定的,但是一部分是不确定的,而确定的部分在使用不确定的部分时就将不确定的部分暴露出去,由该类的子类去完成。这样可以提高扩展性和复用性。注意:暴露出去的方法不一定抽象,有时会有默认的实现方式。

    8.9接口

    接口:初期理解,可以认为是一个特殊的抽象类,当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表示。

    接口定义时,格式特点:

    1. 接口中常见定义:常量,抽象方法
    2. 接口中的成员都有固定修饰符(常量:public static final;方法:public abstract)

    总结:接口中的成员(包括成员变量和方法)都是public的

    如下,即使少写了成员上的,interface也会进行默认的补全。

    但是,一般建议写全:

    接口也是不可能创建对象的,因为接口中的方法都是抽象的,而抽象方法需要被子类实现。即子类对接口中的抽象方法全都覆盖后,子类才可以实例化,若子类只是实现了父类,却没有覆盖父类的方法,那么子类仍是一个抽象类。如下图:

    如上图中,实现类Test实现了Inter接口后,覆盖了父类接口中的show()方法。因此Test类才可以被实例化。

    接口可以被类多实现,也是对多继承不支持的转换形式,java不支持多继承,但是支持多实现。如下图:

    java不支持多继承是因为父类当中的方法有重复,多继承会导致子类调用时出现冲突,为什么接口不会出现这种问题?

    因为多实现接口中,接口没有方法主体,实现类在实现时,可以重写方法体,即使出现接口中出现多个相同方法,实现类中一个方法就可以全部重写多个接口中的那些相同方法。如下图所示:

    注意:多个接口的相同方法的返回值必须一致。

    类和接口相互之间的关系:

    • 类与类之间:继承
    • 类与接口之间:实现
    • 接口与接口之间关系:继承关系

    接口与接口之间可以多继承(因为接口之间没有方法体,方法之间不会冲突)。所以java中也存在多继承,只是多继承只存在在接口与接口之间,而不存在与类与类或者类与接口之间。

    接口的特点:

    1. 接口是对外暴露的规则
    2. 接口是程序的功能扩展
    3. 接口可以用来多实现(降低了耦合性)
    4. 类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
    5. 接口与接口之间可以有继承关系

    9、多态

    9.1概念

    多态:可以理解为事物存在的多种体现形态

    人:男人、女人

    动物:猫、狗

    猫 x = new 猫();

    动物 x = new 猫();

    函数也具有多态性:重载和覆盖

    本节主要内容:多态的体现;多态的前提;多态的好处;多态的应用等。

    9.2多态—扩展性

    需求:动物(猫、狗)

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		Cat c = new Cat();
    		function(c);
    		function(new Dog());
    	}
    	
    	public static void function(Cat c){
    		c.eat();
    	}
    	public static void function(Dog d){
    		d.eat();
    	}
    }
    

    由例可发现,例中只有继承,如果再要加其他动物,还得写其他动物的类并继承动物类,且还要重写动物的吃方法。

    分析发现,所有动物都具有吃的行为,因为他们都是动物。如下图:

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		/*Cat c = new Cat();
    		function(c);
    		function(new Dog());*/
    		Animal a = new Cat();
    		a.eat();
    	}
    	
    	public static void function(Cat c){
    		c.eat();
    	}
    	public static void function(Dog d){
    		d.eat();
    	}
    }
    

    总结:多态的体现:父类的引用指向了自己的子类对象(Animal a = new Cat())。

    父类的引用也可以接收自己的子类对象(提高代码的扩展性:以后也其他的猪、老虎类,只要有这个类继承动物类即可)。所以可以通过动物类,去new其子类(即多态),传入什么动物,便由其自己去调用自己的吃方法。如下图:

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		function(new Cat());
    		function(new Dog());
    	}
    	public static void function(Animal a){
    		a.eat();
    	}
    }
    

    多态的好处:大大提高了程序的扩展性

    多态的前提:必须是类与类之间有关系(继承或者实现);通常还有一个前提,存在覆盖。

    多态的弊端:提高了扩展性,但是只能使用父类的引用访问父类的成员(如Animal的eat()方法)。

    9.3多态—转型

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		
    		Animal a = new Cat();//引用数据类型的类型提升,也称为向上转型
    	}
    	public static void function(Animal a){
    		a.eat();
    	}
    }
    

    多态:Animal a = new Cat();存在引用数据类型的类型提升,即向上转型。

    如果想要调用猫的特有方法时,要如何操作?

    解决:可以向上转型,也可以向下转型,强制将父类的引用,转成子类类型。如下例:

    Animal a = new Cat();

    a.eat();

    Cat c = (Cat)a;

    a.catchMouse();//调用子类特有方法

    注意:Animal a = new Animal();

    Cat c = (Cat)a;//这里是不能强转的,这里的动物类是不明确到底是哪个动物类的

    千万不要出现以下操作:即将父类对象转成子类类型。能转换的是父类引用指向了自己的子类对象时,该引用可以被提升(运行结果是子类结果),也可以被强转转换(向下转型)

    规律:多态自始至终都是子类对象在变化。

    package com.vnb.javabase;
    
    abstract class Animal{
    	
    	abstract void eat();
    }
    
    class Cat extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("吃鱼");		
    	}
    	
    	public void catchMouse(){
    		
    		System.out.println("抓老鼠");
    	}
    }
    
    class Dog extends Animal{
    
    	@Override
    	void eat() {
          System.out.println("啃骨头");		
    	}
    	
    	public void kanJia(){
    		
    		System.out.println("看家");
    	}
    }
    
    public class DuotaiDemo {
    	
    	public static void main(String[] args) {
    		
    		Animal a = new Cat();//引用数据类型的类型提升,也称为向上转型
    	}
    	public static void function(Animal a){
    		a.eat();
    		if(a instanceof Cat){
    			Cat c = (Cat)a;
    			c.catchMouse();
    		}else if(a instanceof Dog){
    			Dog d = (Dog)a;
    			d.kanJia();
    		}
    	}
    }
    

    a instanceof Cat 判断a是否属于Cat这个引用类型。

    一般只用于传的类型是有限的;或传的类型的该类的所属类型,并对此类型有所使用。

    9.4 多态—示例(应用)

    需求:基础班的学生:学习、睡觉等

    高级班的学生:学习、睡觉等

    可以将这两类事物进行抽取。

    9.5多态中成员的特点

    运行结果:

    在多态中(父类有指向子类对象时)成员函数的特点:

    • 在编译时期,参阅引用型变量(Fu类)所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败;
    • 在运行时期,参阅对象(Zi类)所属的类中是否有调用的方法(静态方法除外)。

    对于Fu f = new Zi();后调用子父类中的方法说明:子父类中都有此方法,使用子类中的方法(子类覆盖了父类方法);子类中没有此方法,调用父类的此方法(子继承父类);Fu类中没有此方法,编译失败。如下图(method3会编译失败):

     多态中,成员变量的特点,如下图:

    输出结果:

    在多态中,成员变量的特点:无论编译还是运行,都参考左边(引用型变量所属的类)(此情况一般只会面试出现)。因为有父类是,会先加载父类去拿num。

    打印结果:(开发一般不会出现这种情况)

    所以静态时不会出现多态中成员函数的特点。

    总结:在多态中,静态成员函数的特点:无论是编译时期还是运行时期,都是参考左边。

    因为静态方法存在方法区中,不需要对象,只需要Fu.method4()即可,看的是引用型对象加载的内容(Fu),而不是对象(Zi),只要父类型引用还在,用的还是父类引用,而不是对象。如下图:

    当调用f.method1()时,f确实执行的是对象,method1()被运行的时候是被对象运行,对象在调用非静态方法时,访问的是对象中的数据;但是静态方法method4()本身不访问对象特有的数据,用类名调用的话,它只看所属引用型(Fu)变量中的数据类型,即静态区中的方法,它不参考右边非静态区的方法。如下图:

    动态静态绑定:

    当method4()方法一进内存,因为是静态方法,就已经被绑定在方法所属的类的引用上(Fu),即method4属于Fu。而静态区上,this是动态的,this指向哪个对象就代表哪个对象。而现在写的是Fu f = new Zi(),f.method1(), new的还是Zi,所以运行的还是子类的方法(动态绑定);当Fu f = new Fu()此时就找的是Fu类上的方法。

    多态总结:

    9.6多态的电脑的主板示例

    需求:电脑运行示例,电脑运行是基于主板

    上图中,主板就可以跑起来了

    但是发现问题:运行一段时间后,想上网,想听音乐,主板没有此功能,因此就需要给主板提供扩展性的功能(原来功能毫无扩展性)。如下图:

    该类在内存中表示:

    为降低网卡声卡等和主板的耦合性,会在主板上留一个槽,并约定这些槽的规则,此后,只要符合这个规则,就可以在主板上运行。而这些插槽即接口的编写。如上图的PCI即设定的某些规则。代码如下图所示:

    PCI p = new NetCard();//接口型引用指向自己的子类对象。

    如上图,网卡声卡等都需要符合规则才能用,所以都需要实现PCI接口。

    9.7多态的扩展示例

    需求:对数据库的操作

    数据是:用户信息

    1. 连接数据库 JDBC Hibernate
    2. 操作数据库 CRUD
    3. 关闭数据库连接

    问题:某天发现JDBC太麻烦,需要使用某种框架进行连接数据库,按照以上方法,就需要修改所有方法。如下图:

    内存中:

    但是发现,主程序也需要改,这样代码耦合性太高,所以需要重新设计代码。

    分析:无论如何连接数据库,最后要的只是CRUD,无论采用哪种方式连接,内部的实现方式仍然相同,所以可以专门定义出数据库连接的规则。如下图,interface接口,事先定义好CRUD的接口。之后再需要使用其它数据库连接方式时,也只需要实现该接口即可。

    由此发现,连接数据库的程序和主程序不再有强的耦合关系。

    9.8 Object类 —equals()

    Object:是所有对象的直接或者间接父类,该类中定义的肯定是所有对象都具备的功能。

    java指定所有对象都具备比较性,都能比较两个对象是否相同。此方法定义在Object中,即equals()方法。

    例如:

    equals()方法比较的是对象的地址值。equals()里的参数不确定,所以使用Object,即可多态实现比较。

    自定义比较方法:

    使用“==”对象不同,数据相同即为真,如下图:

    问题:Demo中已经有了比较的方法equals()(超类Object),就不需要重新比较了(功能一致,可以沿袭父类,写自己特有的功能(复写(覆盖)equals()))。如下图:

    Object中没有定义过num,如果不向下转型,是不能进行编译的。

    问题,当传入的对象不同时,运行时出现不可转换错误。解析:一般是自己的对象和自己比较,不是则直接返回false。如下图:

    输出false。

    9.9Object类—toString()

    java 任意对象都可以通过toString()方法返回字符串,所以类.toString()就可以拿到对象的hash值。而hashCode方法也可以返回对象的hash值。

    图中Demo如何得到?

    一个对象的建立要根据类文件进行建立。一个类文件中包含名称、很多构造函数、一般方法等很多东西,如何获取到某个构造函数,只有这个对象最明确,所以可以通过Class来描述这些class文件,通过  类.getClass() 进行获得。

    类中有Class.getClass()方法,可以通过类.getMethods()获取到类文件中所有的方法。

    10、内部类

    10.1内部类概念

    将一个类定义在另一个类的里面,而里面那个类就称为内部类(内置类、嵌套类)。

    内部类访问特点:内部类可以直接访问外部类中的成员,包括私有成员;而外部类要访问内部类中的成员必须要建立内部类的对象。

    好处:可以直接访问外部类中的成员(包括私有成员),而不用创建对象。

    外部能不能直接访问内部类中的方法(function)?

    分析:inner不是独立存在,而是在Outer中,所以使用inner所在类.inner即可(很少用,因为内部类很可能会被private修饰(当内部类是外部类的成员时))。如下图:

    为什么内部类能直接访问外部类中的内容?

    输出结果为6。

    当内部外部存在变量相同时,优先获取内部变量;

    想拿到4(内部类的变量),使用this.x;想要拿到外部类的x=3,可用Outer.this.x。如下图:

    之所以可以直接访问外部类中的成员是因为内部类中持有了一个外部类的引用,该引用写法是”外部类名.this”。

    10.2静态内部类

    访问格式:

    • 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部对象。格式:外部类名.内部类名  变量名 = 外部类对象.内部类对象(Outer.Inner in = new Outer().new Inner())
    • 当内部类在成员位置上,就可以被成员修饰符所修饰。比如,private:将内部类在外部类中进行封装,Static:内部类就具备了静态的特性。当内部类被静态修饰后,只能直接访问外部类中的静态成员了(外部成员也要用static修饰才行)。修饰出现限制了。如下图:

    修改后:在外部其他类中,如何直接访问static内部类的非静态呢?new Outer.Inner().function();如下图:

    在外部其他类中,如何直接访问static内部类的静态呢?

    new Outer.Inner.function();但是这种方式使用得很少。

    注意当内部类中定义了静态成员,该内部类必须是静态的(static)。如下例:

    即当function()方法被static修饰时,Inner也必须是静态修饰

    当外部类中的静态方法访问内部类时,静态类也必须是静态的。如下图:

    改为以下即可:

    10.3内部类定义原则

    类是用来描述现实中事物的,当描述事物时,事物内部还有事物,该事物就用内部类来描述,因为内部事物在使用外部事物中的内容(成员和函数等)。

    示例:描述人体,在描述心脏时,认为心脏也是一个功能,但是心脏描述比较复杂,有跳动、血液等等众多属性,对于这些属性就得用类进行描述,因为,心脏可以访问人体中其他部分,所以,将心脏定义成内部类最合适(内部类:类中包含另一个类),而且心脏不能直接被外部访问,但是提供外部访问的功能。

    10.4匿名内部类

    只有定义在成员位置上,才能被私有或静态所修饰。内部类可以写在类的任意位置上(成员、局部成员(变量或方法)等)

    如下例,访问规则没变:

    上例已经不能被静态私有修饰,Inner在局部方法里,在这里也不能使用static。局部内部类不能用static修饰,非静态没对象不会运行(必须有调用的地方new Inner().function(),如下)。

    方法中的局部变量,内部类在局部,那么内部类就能访问局部变量?

    修改后:

    有上例可知,

    1. 内部类定义在局部时,不能被成员修饰符修饰;
    2. 可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部变量,只能访问被final修饰的局部变量

    如上图,方法中的参数也必须是final。

    如何只有一个对象实现多次调用method(),如下图:

    调用method()时a=7进栈,执行完后,会释放掉,再调用method()a =8,所以不会冲突。

    匿名内部类:

    1. 匿名内部类其实就是内部类的简写格式
    2. 定义匿名内部类的前提:内部类必须继承一个类,或者实现接口

    将下列例子简化成匿名内部类:

    简化后:匿名了,没有Inner类了,怎么new对象?如下:

    可以使用 new AbsDemo(){

                      void show();

                    }

    AbsDemo本身是抽象类,不能new,但是可以像下面这样写,在复写掉里面的方法。红框的内容是一个对象,是AbsDemo的子类对象(只有子类才可以复写父类的抽象方法)。

    匿名内部类调用本匿名内部类中的方法:

    1. 总结:匿名内部类的格式,new 父类或者接口(构造函数,可以往其传参){定义子类的内容}
    2. 其实匿名内部类就是一个匿名子类对象,可以理解为带内容的对象

    匿名内部类中也可以有子类特有的方法,不一定是覆盖父类的方法,但是,不可以同时调用show和abc方法(匿名内部类只能调用一次)。如下图:

    但是可以,建立多次匿名对象:

    简化,可以将多个匿名内部类取名(利用多态),如下图:

    d.show()可以是父类的方法,d.abc()不能调用,多态中的父类没有abc()方法。

    注:匿名内部类中定义的方法最好不要超过3个。可读性差。

    匿名内部类练习:

    如上图,Test.function:Test类中有一个静态方法function,.method():function这个方法运算后的结果是一个对象。而且是一个Inter类型的对象。因为是Inter类型的对象,才可以调用method()方法。

    Test.function().method();即等同于:

    Inter in = Test.function();

    in.method();

    什么时候用匿名内部类?

    当使用的方法参数类型是接口时,可以再调用方法时传一个匿名内部类(如AWT匿名监听器等)。如下图:

    若没有父类也没有抽象类或接口,想写个匿名内部类,怎么写?

    使用所有类的父类(Object对象),如下图:

    11、异常

    11.1异常概述

    异常:程序运行时出现不正常情况。

    异常由来:问题也是现实生活中的一个具体事物,也可以通过java的类的形式来进行描述,并封装成对象。异常其实就是java对不正常情况进行描述后的对象体现。

    对于问题的划分:一种是严重的问题;一种是非严重的问题。

    对于严重的,java通过Error类进行描述。对于Error类问题一般不编写针对性的代码进行处理;对于非严重的问题,java通过Exception类进行描述,对于Exception类的问题可以使用针对性的处理方式进行处理。

    无论Error或者是Exception都具有一些共性内容:不正常情况的信息、引发原因等,会向上抽取,抽取出来的即为:

    Throwable

         |--Error

         |--Exception

    API解释:

     

    Error异常:

    Exception异常:

    11.2异常try catch

    java 有内置的异常处理方式,只要有某些异常就会进行处理。java提供了特有的语句处理方式,即

    try{

      需要检测的代码

    }catch(异常类 变量){

      处理异常的代码(处理方式)

    } finally{

      一定会执行的语句

    }

    例如:

    在方法div()中,捕获到异常后封装成new AritchmeticException()后,会抛给main中的有try进行异常捕获newAritchmeticException()异常,捕获到后不会继续往下执行,而是有catch到(Exception e = new ArithmeticException();),再执行catch中的语句。

    对捕获到的异常对象进行常见方法操作:

    • String getMessage()  打印异常信息
    • String toString()  打印异常名及信息
    • void printStackTrace();  异常信息、类名及异常出现位置

    其实JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。

    11.3异常声明throws

    编写功能时,不清楚是否调用者会传入正确的参数,所以,可以在功能上通过throws的关键字声明该功能有可能会出现问题。例:

    编译时会出现以下错误:

    但是如果不抛出,编译时不会出现问题,而运行会报错:

    如果一直抛出,直至抛给JVM,则会使用JVM默认的异常处理机制:

    抛异常和捕捉异常是有使用场景的。

    11.4多异常处理

    对多异常的处理:

    • 声明异常时,建议声明更为具体的异常,这样处理得更具体

    • 原则:对方声明几个异常就有几个catch块,不要定义多余的catch块,如果多个catch块中的异常出现继承关系,则父类异常catch块放在最下面。建议在进行catch处理时,catch一定要定义具体的处理方式,不要简单定义依据e.printStackTrace(),也不要简单的书写一条输出语句。做法:用硬盘文件记录异常日志文件。

    也可以抛出父类异常Exception:

    若出现了,意料之外的异常,应该是停止执行,让我们发现问题,进行针对性处理,而不是通过Exception进行。如下图:

    11.5自定义异常

    因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象,所以对这些特有的问题,可以按照java的对问题封装的思想,对本项目中的特有问题进行自定义异常封装。

    需求:在本程序中,对于除数是-1,也视为是错误的无法进行运算。那么就需要对这个问题进行自定义的描述。

    当函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作,要么在内部try catch;要么在函数上声明让调用者处理。一般情况在函数内出现异常,函数上需要声明。

    打印结果:

    发现打印结果中只有异常名称却没有异常信息,因为自定义异常并未定义所属信息,那么如何定义异常信息?

    打印结果:

    问题:发现父类构造函数中有个带message的方法

    父类中已经定义了构造函数接收异常信息,所以就会有变量接收这个信息,子类在继承这个类时,只要new Exception()就能获取到这些信息。类似下例:

    所以,异常类如下:

    总结:因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时将异常信息通过super语句传递给父类,就可以直接通过getMessage()获取异常信息。

    自定义类,根据特有数据定义异常,如异常类名、异常的值等,如下图:

    输出结果:

    自定义异常:必须是自定义类继承Exception类。

    为什么继承Exception:

    异常体系有一个特点,因为异常类和异常对象都需要被抛出,他们都具备可抛性,这个可抛性是Throwable这个体系中的独有特点,只有这个体系中的类和对象才可以被throws和throw操作。只有继承Throwable、Exception、Error,只有在这个体系中才能使用throws和throw进行抛出异常。

    11.5异常—throws和throw的区别

    throws和throw的区别:

    1. throws使用在函数上,throw使用在函数内;
    2. throws后面跟的异常类,可以跟多个,用逗号隔开。throw后面跟的是异常对象(throw new 异常类)

    11.6RuntimeException

    ArithmeticException类构造函数有定义message的方法,可以直接使用这个构造方法打印错误信息:

    打印结果:发现函数内抛了,但是函数上并没有声明过,但是编译通过了。

    但是,当抛Exception时发现有安全隐患,编译时直接报错。

    原因:

    由API可发现ArithmeticException异常类的父类是RuntimeException异常,即运行时异常。该异常很特殊。RuntimeException或者RuntimeException的子类如果在函数内抛出了该异常,函数上可以不用声明异常,编译一样通过。如果在函数上声明了该异常,调用者可以不用进行处理(catch或throws),编译一样通过(如下例)。

    原因:之所以不用再函数上声明是因为不需要让调用者处理,当该异常发生,希望程序直接停止。因为在运行时出现了无法继续运算的情况,希望停止程序后由程序员对代码进行修正。如int x = 3/0,一旦3/0被允许执行了,那么x将无法得到正确的值,因此必须对3/0进行正确处理才行

    示例:

    如上例,发现当name传null时,很容易出现空指针异常,我们再出现name传null时,必须要程序员进行修改才行。因此要让其抛异常才行。而且不能在方法上使用throws,让其抛出去。如下图:

    总结:自定义异常时,如果该异常的发生,无法继续进行运算,就让自定义异常继承RuntimeExcetion。

    所以自定义异常示例可改为:

    输出结果:

    对于异常分两种:

    1. 编译时被检测的异常(javac编译时,发现方法中抛出了非RuntimeException及其子类,而方法上没有标识throws,就会认为有安全隐患。此异常时可处理的,要标识出去,让调用者进行对应的处理;如果函数上标识了throws,函数的调用者也必须进行处理,try或抛);
    2. 编译时不被检测的异常(运行时异常:RuntimeException及其子类)(方法内抛throw,但是方法上不标识throws,它会拿着异常对象去判断是否是运行时异常(e instanceof RuntimeException),如果是,无论标识与否都不管)

    11.7 异常练习(应用)

    需求:毕老师用电脑上课

     

    异常:电脑宕机、电脑蓝屏

    要对问题进行描述,封装成对象

    根据电脑操作状态的不同,导致不同问题的产生。

     

    电脑冒烟了,不能抛出去,因为抛出去了其他老师也处理不了,但是当冒烟出现问题后,讲课进度无法继续,出现了讲课进度无法完成的问题,所以虽然捕捉到的是MaoYanExcetion,但是要把它封装成我的问题再跑出去(讲课无法完成问题)再抛出去

    注意:throw后面不会继续执行,throw是函数结束的标志。

    11.7异常—finally

    finally块的代码无论如何都会执行,通常用于释放资源

    示例:数据库连接(数据库连接有限,一旦占用不释放,其他人就会一直连不上)

    连接数据库;数据库操作(throw new SQLException());关闭数据库(无论操作是否成功一定要关闭资源)

    数据没有存储成功也是异常,SQLException我们处理不了,但是数据没有存储成功是可以处理的(分层思想,模块式开发):

    11.8异常—处理语句其他格式

    以下,因为throw new Exception()语句,已经被catch捕捉处理掉,所以语句正确

    catch是处理异常,如果没有catch就代表异常没有被处理,如果该异常时检测时异常,那么必须声明。

    11.9异常—覆盖时异常的特点

    异常在子父类覆盖中的体现:

    • 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。

    父类已经有问题了,子类要继承父类的话不能比分类还有问题,只能跑父类或者父类的子类的异常

    有一个子类覆盖父类,继承CException,且Test中传入子类对象:

    这样程序会挂,编译就是失败。

    • 如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集
    • 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常;如果子类发生了异常,就必须要进行try处理,绝对不能抛

     

    10.10异常—练习

    有一个圆形和长方形,都可以获取面积,对于面积如果出现非法的数值,视为获取面积出现问题,问题通过异常来表示。

    基本设计:

     

    当传入负数时,面积为负数,无意义

    之前处理方式:

    会发现正常流程代码和问题处理代码结合比较紧密,阅读性差。异常的产生可以让流程处理代码和问题处理代码分离。

     

    输出:

    如果真的出现问题,一旦为负数,就没意义,不会继续执行,所以直接使用RuntimeException即可,也不用进行标识。主函数中也就不需要catch了

    求圆的面积:写RuntimeException也可以,但是,此名称跟程序意义无关,不能见名知意

    所以还是写自定义异常比较直观:

    11.11异常—总结

    异常:是对问题的描述,将问题进行对象的封装。

    异常体系:

    Throwable

    |--Error

    |--Exception

       |--RuntimeException

    异常体系的特点:异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作。只有异常体系具备这个特点。

    throw和throws的用法:

    throw定义在函数内,用于抛出异常对象,throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开。

    当函数内容有throw抛出异常对象,并未进行try处理。必须在函数上声明,否则编译失败。注意RuntimeException除外。也就是说,如果函数内抛出的是RuntimeException异常,函数上可以不用声明。

    如果函数声明了异常,调用者需要进行处理,处理方式可throws可以try。

    异常有两种:一种叫做编译时被检测异常(该异常在编译时,如果没有处理(没有抛也没有try),那么编译失败,该异常被标识,代表着可以被处理);一种叫做运行时异常(编译时不检测)(在编译时,不需要处理,编译器不检查,该异常的发生,建议不处理,让程序停止,需要对代码进行修正)

    异常处理语句:

    try{

      需要被检测的代码

    }catch(){

      处理异常的代码

    }finally{

      一定会处理的代码

    }

    有三种结合格式:

    try{

    }catch(){

    }

    try{

    }finally{

    }

    try{

    }catch(){

    }finally{

    }

    注意:

    1. finally中定义的通常是关闭资源代码,因为资源必须要释放。
    2. finally有一种情况读不到:即系统退出(System.exit(0)虚拟机结束时,finally不会再执行)

    自定义异常:

    定义类继承Exception或者RuntimeException

    1. 为了让该自定义类具备可抛性
    2. 让该类具备操作异常的共性方法

    当要定义自定义异常的信息时,可以使用父类已经定义好的功能。异常信息传递给父类的构造函数

    class MyException extends Exception{

    MyException(String message){

      super(message);

    }

    }

    自定义异常:

    按照java的面向对象思想,将程序中出现的特有问题进行封装

    自定义异常好处:

    1. 将问题进行封装
    2. 将正常流程代码和问题处理代码相分离,方便阅读

    异常的处理原则:

    1. 处理方式有两种:try 或者throws
    2. 调用到抛出异常的功能时,抛出几个,就处理几个(不多抛不多处理)。一个try对应多个catch的情况。
    3. 多个catch,父类的catch放到最下面
    4. catch内需要定义针对性的处理方式,不要简单的定义printStackTrace输出语句,也不要不写。当捕获到的异常,本功能处理不了时,可以继续在catch中抛出即:

    try{

      throw new AException();

    }catch(AException e){

      throw e;

    }

    如果该异常处理不了,但并不属于该功能出现的异常,可以将异常妆花后,再抛出和该功能相关的异常。

    或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道,并处理。也可以将捕获异常处理后,转换为新的异常

    try{

      throw new AException();

    }catch(AException e){

      //捕获到AException但是AException处理不了,可以转换为新的异常处理类

     throw new BException();

    }

    比如,汇款的例子

    异常的注意事项:

    在子父类覆盖时:

    1. 子类抛出的异常必须是父类的异常的子类或者子集;
    2. 如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛

    参阅:ExceptionTest.java 老师用电脑上课

          ExceptionTest1.java图形的面积

    11.12异常—练习四

    如上图,首先执行main中的func()方法,调用到func(),执行try抛异常,执行finally中B,再由main()中捕捉到异常执行main中catch得到C,A在func()有异常后不再执行,再执行D,所以,输出为B  C  D。

    如上图,main方法中首先执行new Demo后,执行到默认的super()方法(默认有,没有写而已),从而执行到其父类Test类,输出Test,然后输出Demo,在执行main中new Test(),输出Test。

    在多态中(父类有指向子类对象时)成员函数的特点:

    在编译时期,参阅引用型变量(Fu类)所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败;//此处A接口中没有定义func()方法,所以编译失败

    在运行时期,参阅对象(Zi类)所属的类中是否有调用的方法。

    如上图,首次执行到for循环时,f.show(‘A’)执行Demo类中Demo()方法,打印A输出false,而f.show(‘B’)也执行Demo类中的Demo()方法打印B输出false满足条件,(i<2)不再执行,因此循环直接结束。(在运行时期,参阅对象(Zi类)所属的类中是否有调用的方法。)。

    如上图,A接口中没有定义Test方法,编译失败。

    如上图,执行main中new Demo(“A”)后,执行到Demo类中构造方法,而其中会有个默认的super()方法,从而执行到父类Super中的狗仔函数Super(),从而打印B,执行i+=2,然后继续执行Demo类中Demo()构造函数中代码,打印C,将i赋值为5,然后继续执行mian(),从而打印d.i  为5

    如上图,定义匿名内部类的前提:内部类必须继承一个类,或者实现接口。

    interface Inter{

    void show(int a,int b);

    void func();

    }

    class Demo {

    public static void main(String[] args){

      Inter in = new Inter(){

        public void show(int a,int b){}

        public void func(){}

    }

    in.show();

    in.func();

    }

    }

    匿名内部类一定要在方法上加上public标识共有;

    要调用匿名内部类的多个方法,使用Inter in = new Inter(){}格式后,用实例名in调用即可

    如上图,编译失败,非静态内部类,不可以定义静态成员;

    内部类中如果定义了静态成员,该内部类必须被静态修饰

    如上图,读清楚题目,是存在于Demo的子类中,不是再同一个函数的重载

    A 可以,覆盖

    B 不可以 权限不够

    C 可以  和父类不是一个函数。没有覆盖相当于重载

    D 不可以,因为该函数不可以和给定函数出现在同一类中,或者子父类中

    E 不可以,静态只能覆盖静态

    this:代表本类对象,哪个对象调用this所在函数,this就代表哪个对象

    final:

    1. 修饰类,变量(成员变量、静态变量、局部变量),函数
    2. 修饰的类不可以被继承
    3. 修饰的方法不可以被覆盖
    4. 修饰的变量是一个常量,只能赋值一次
    5. 内部类只能访问局部的final形式变量

    11.

    如上图,输出结果:4  5  showZi  showZi

    成员变量看左边,变量不存在覆盖;

    方法看右边

     

    如上图,定义成员变量,然后计算和

    局部接收进来的,值改变后,其他方法也可以用

    如上图,输出BCD

    如上图,编译失败,应该父类中缺少空参数的构造函数;或者子类应该通过super语句指定要调用的父类中的构造函数

    如上图,编译失败:因为子父类中的get方法没有覆盖,子类调用的时候不能明确返回值是什么类型,所以这样的函数不能存在在子父类中。

    如上图,编译失败:因为打印字符串“A”的输出语句执行不到。

    如上图,16题和13题区:把抛出异常封装在方法中,代表有可能有问题,有可能没有问题,所以13题中的A是有可能执行到的,但是16题中A绝对不可能执行到。

    如上图,A ok 

    B 不可以,因为主函数是静态的,如果要访问inner需要被static修饰

    C 错误,格式错误

    D 错误 因为inner不是静态的

    编译失败,多个catch时,父类的catch要放在最下面。

     

     

    如上图,方法时静态的,所以调用多次后,前面一次方法不会销毁,且output也是共享的不会销毁,foo(0)执行后,在执行foo(1)时继续累加

    return语句后,finally中的语句继续执行,但是finally外的不会继续执行

    所以结果是 13423         

     

    已经做过

     

     

    输出4

     

    已经做过

    如上图,数组查找,数组可能无序,所以不要折半

     

    如上图,用到本类对象this

    前面的cir用this表示,cir2传入

    12包

    12.1包package

    1. 对类文件进行分类管理
    2. 给类提供多层命名空间
    3. 写在程序文件的第一行
    4. 类名的全称是 包名.类名
    5. 包也是一种封装形式

    命令中加参数用于创建包的目录

    javac –d . PackageDemo.java

    -d 指定包所存放的位置

    . 表示当前目录

    PackageDemo.java 类名

    执行时:

    java pack.PackageDemo

    不存在当前目录(而存放到指定目录):

    javac –d c:\ PackageDemo.java

    set classpath–c:\  指向包(pack)的父目录即可

    java pack.PackageDemo

    包的出现可以让java的类文件和class文件相分离,当别人要执行时,可以只给class运行文件,而不用给源文件

    12.2包与包之间的访问

     

    先编译DemoA,再编译PackageDemo

    编译时将编译文件存在存一个路径

    发现报错。

    错误原因:类名写错

    因为类名的全名是:包名.类名

    改正后:

    发现又报错:

    错误原因:软件包不存在

    packa包不再当前目录下,需要设置classpath,告诉JVM去哪里找指定的packa包

    包找到了,但是又发现其他错误

    有了包,范围变大,一个保重的类要被访问,必须要有足够大的权限,所以要被访问的类需要被public修饰

    因为DemoA类的修饰符没有权限

    类的修饰符有两个:protected、public

    改完后发现其他错误:

    错误原因:类共有后,被访问的成员也要共有才可以被访问

    改正后,运行正确

    总结:

    • 包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰
    • 不同包中的子类还可以直接访问父类中被protected权限修饰的成员(java给包与包中的方法提供权限protected权限(默认),使其不继承也可以拿到某类中的方法)

    包与包之间使用的权限只有两种:public和protected(只能给子类用)

     

    public

    protected

    default

    private

    同一类中

    Ok

    Ok

    Ok

    Ok

    同一包中

    Ok

    Ok

    Ok

     

    子类

    Ok

    Ok

     

     

    不同保重

    Ok

     

     

     

    一个java文件里不能出现两个公有类或接口:(以下代码错误)

    可以将两个文件放在同一个包下

    12.3导入import

    为了简化类名的书写,使用一个关键字:import

    同一目录下有多个类时,使用*,导入到文件

    import导入的是包中的类,如果有包是不能导入的

    建议不要写通配符*,需要用到包中的那个类就导入哪个类,否则占用内存太多

    导入不同包的同名的包时,必须写清楚类的全名(包名+类名)

    如上例,packa和backb包中存在同名的DemoC类时,使用时,必须写DemoCracy的全名packa.DemoC c = new packa.DemoC();

    建议定义包名时不要重复,可以使用URL来完成定义,URL是唯一的

    12.4jar包

    java的压缩包

    1. 方便项目的携带
    2. 方便与使用,只要在classpath设置jar路径即可
    3. 数据库驱动,SSH框架等都是以jar包体现的

    java打jar包需要借助java JDK工具java.exe

    jar命令的用法:

    如:

    -c 创建新的归档文件

    -f 创建的归档文件名

    haha.jar jar包名字

    packa pack 需要打成包的文件夹(注:命令必须在当前文件夹下执行,如例两个文件夹在myclass文件下)

    jar包和打成其他包的区别:

    jar –tf haha.jar  查看归档目录

    存放了java特有的配置文件,且

    目录下存放的文件都删除了,双击jar包时仍然可以执行的。如下,

    使用jar –cvf a.jar packa pack  可以显示打印时的详细信息

    jar –tvf a.jar packa pack 可以显示时间等详细信息

    数据重定向

    将打包时的数据定向到1.txt文件夹内:

    将jar包中所有的类重定向放到rt.txt文件中

    展开全文
  • 如何理解Java中的面向对象

    万次阅读 多人点赞 2018-08-01 14:28:30
    &nbsp;&nbsp;...我的理解是:面向对象是向现实世界模型的自然延伸,这是一种”万物皆对象”的编程思想。在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。 &

           好几次面试都问到了这个问题,回答的也都不好,暂且总结一下:
           我的理解是:面向对象是向现实世界模型的自然延伸,这是一种”万物皆对象”的编程思想。在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。
           面向对象有三大特性:封装、继承、多态。
           (1)封装。是将一类事物的属性和行为抽象成一个类,一般是使其属性私有化,行为公开化,提高了数据的隐秘性的同时,使代码模块化。这么做的好处是:
                  • 将变化隔离。
                  • 便于使用。
                  • 提高代码复用性。
                  • 提高安全性。
           封装原则:
                  • 将不需要对外提供的内容都隐藏起来。
                  • 把属性都隐藏,提供公共方法对其访问。
    Java中可以通过对类的成员设置一定的访问权限,实现类中成员的信息隐藏。

    private:类中限定为private的成员,只能被这个类本身访问。如果一个类的构造方法声明为private,则其它类不能生成该类的一个实例。
    default:类中不加任何访问权限限定的成员属于缺省的(default)访问状态,可以被这个类本身和同一个包中的类所访问。
    protected:类中限定为protected的成员,可以被这个类本身、它的子类(包括同一个包中以及不同包中的子类)和同一个包中的所有其他的类访问。
    public:类中限定为public的成员,可以被所有的类访问。
    访问权限
           (2)继承。基于已有的类的定义为基础,构建新的类,已有的类称为父类,新构建的类称为子类,子类能调用父类的非private修饰的成员,同时还可以自己添加一些新的成员,扩充父类,甚至重写父类已有的方法,更其表现符合子类的特征。
           Java中父类可以拥有多个子类,但是子类只能继承一个父类,称为单继承。
           继承的好处是:
                  • 实现了代码的复用。
                  • Java中所有的类都是通过直接或间接地继承java.lang.Object类得到的。
                  • 子类不能继承父类中访问权限为private的成员变量和方法。
                  • 子类可以重写父类的方法,即命名与父类同名的成员变量。

           Java中通过super来实现对父类成员的访问,super用来引用当前对象的父类。super 的使用有三种情况:
                  • 访问父类被隐藏的成员变量,如:super.variable;
                  • 调用父类中被重写的方法,如:super.Method([paramlist]),super())调用父类构造方法;
                  • 调用父类的构造函数,如:super([paramlist]);

           super和this的用法相同:this代表本类应用 ;super代表父类引用 。当子父类出现同名成员时,可以用super进行区分 ,子类要调用父类构造函数时,可以使用super语句。
           在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。

    注意:

           1 . 子类中所有的构造函数默认都会访问父类中空参数的构造函数,因为每一个构造函数的第一行都有一条默认的语句 super();子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。当父类中没有空参数的构造函数时,子类的构造函数 必须通过this或者super语句指定要访问的构造函数。

           2 . 覆盖时,子类方法权限一定要大于等于父类方法权限静态只能覆盖静态。父类中的私有方法不可以被覆盖。

           3.被final修饰的类是一个最终类,不可以被继承。
                 被final修饰的方法是一个最终方法,不可以被覆盖。
                 被final修饰的变量是一个常量,只能赋值一次。

           4.内部类只能访问被final修饰的局部变量。
           (3)多态。方法的重写、重载与动态连接构成多态性。如果说封装和继承是为了使代码重用,那么多态则是为了实现接口重用。多态的一大作用就是为了解耦–为了解除父子类继承的耦合度。如果说继承中父子类的关系式IS-A的关系,那么接口和实现类之之间的关系式HAS-A。简单来说,多态就是允许父类引用(或接口)指向子类(或实现类)对象。很多的设计模式都是基于面向对象的多态性设计的。
           多态性:发送消息给某个对象,让该对象自行决定响应何种行为。

    通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。

      java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

      1. 如果a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。

      2. 如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。要理解多态性,首先要知道什么是“向上转型”。
      
    子类Cat继承了Animal类,那么后者就是前者是父类。

      Cat c = new Cat();//实例化一个Cat的对象,

      Animal a = new Cat();//定义了一个Animal类型的引用,指向新建的Cat类型的对象
      由于Cat是继承自它的父类Animal,所以Animal类型的引用是可以指向Cat类型的对象的。那么这样做的什么意义是:因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特,
      定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。
      所以,父类类型的引用可以调用父类中定义的所有属性和方法,但是对于子类中定义而父类中没有的方法,它是无可奈何的;
      同时,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;
      对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法,这就是动态连接。
      实现多态,有二种方式,覆盖(override),重载(overload)。

           覆盖,是指子类重新定义父类的虚函数的做法。它是覆盖了一个方法并且对其重写,以求达到不同的作用。在覆盖要注意以下的几点:
                  1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
                  2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
                  3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
                  4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

           重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,在使用重载要注意以下的几点:
                  1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
                  2、不能通过访问权限、返回类型、抛出的异常进行重载;
                  3、方法的异常类型和数目不会对重载造成影响;
                  4、对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

           多态也有弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
           参考文章:
                  对于面向对象的简单理解
                  面试总结之谈谈你对面向对象的理解
                  面试——你是如何理解java的面向对象思想

    展开全文
  • Java面向对象编程之三大特性

    千次阅读 2018-11-21 14:02:26
    一:面向对象编程三大特性 (1)继承 (2)封装 (3)多态 二:接口,内部类,抽象类 三:常见关键字:this,super,static,final
  • JAVA面向对象编程题

    万次阅读 多人点赞 2018-03-26 22:27:48
    1.给我一个字符串,例如I love java,输出: java love I public class StringReverse { public void swap(char[] arr, int begin, int end) { while(begin < end) { char temp = arr[b...
  • (二)java面向对象编程(知识模块较完整)

    千次阅读 多人点赞 2018-08-07 15:01:35
    这一篇文章讲的内容不太容易理解,但是同时也是java最为重要的一块理论知识,需要大家好好琢磨清楚。 1.编程语言的发展(这儿大致了解就行):发展是朝着人类更内容理解的方向。 机器语言:直接由计算机的指令组成...
  • Java面向对象试题

    万次阅读 多人点赞 2019-03-19 15:58:05
    1、 请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。...在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方 法、fly()方法。 评分标准: 正确写出Ani...
  • Java 面向对象编程.pdf

    千次下载 热门讨论 2012-12-24 17:41:29
    《Java 面向对象编程》内容由浅入深,紧密结合实际,利用大量典型实例,详细讲解Java面向对象的编程思想、编程语法和设计模式,介绍常见Java类库的用法,总结优化Java编程的各种宝贵经验,深入阐述Java虚拟机执行...
  • Java面向对象

    千次阅读 多人点赞 2019-05-08 23:33:50
    Java面向对象1.Java面向对象是什么2.面向过程和面向对象的优缺点1.面向过程的优缺点:2.面向对象的优缺点:3.面向对象三大特性五大原则1.三大特性:2.五大原则:总结: 1.Java面向对象是什么 OOP(面向对象编程)、...
  • *学生管理系统java面向对象版本

    千次阅读 2018-09-20 16:59:16
    学生管理系统java面向对象版本 鉴于初学者都在用学生管理系统练习业务逻辑,本人也提供一个java写的面向对象版本 技术能力有限,仅供新手学习参考。大神略过。 +++++++++学生类+++++++++++++ package student...
  • Java面向对象设计模式

    千次阅读 2017-05-02 14:17:36
     ——可复用面向对象软件的基础  设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 ...
  • Java面向对象面试题总结

    千次阅读 2019-02-27 17:22:05
    答:(1)在类的定义中设置访问对象属性(数据成员)及方法(成员方法)的权限,限制本类对象及其他类的对象使用的范围。 (2)提供一个接口来描述其他对象的使用方法 (3)其他对象不能直接修改本对象所拥有的...
  • JAVA面向对象是什么?

    千次阅读 2019-05-12 13:13:09
    JAVA面向对象面向对象?What???什么是对象(Object)?什么是面向对象?面向对象有什么特征? 面向对象?What??? 众所周知 Java是一种面向对象的语言,那么什么是面向对象? 什么是对象(Object)? 对象就是具有...
  • Java面向对象知识点总结

    千次阅读 多人点赞 2015-11-29 18:42:02
    一、类和对象 (1)类:类是模子,确定对象将会拥有的特征(属性)和行为(方法)。 类的特点:类是具有相同属性和方法的一组对象的集合。 (2)对象 创建对象:类名 对象名 = new 类名() 使用对象对象.属性 / ...
  • Java面向对象的理解

    千次阅读 2019-04-06 10:10:19
    Java面向对象的理解 1.思想简述: 面向对象是一种思想。它将数据和操作数据的方法封装在对象中,从而使对象有了一些功能,也就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的...
  • Java面向对象思维导图总结

    千次阅读 2018-11-26 10:43:53
    自己学习总结了一些思维导图(持续更新中),后面附有GitHub链接 ,分享给大家。https://github.com/panjianlong13/MindMapSummary  
  • JAVA中,万事万物皆对象,现在我有一个问题:JAVA中的执行语句是对象吗?如果是,它的class是什么?
  • JAVA面向对象知识点详解

    千次阅读 多人点赞 2016-12-15 14:13:27
    一、类和对象 类:类是对对象的一种抽象描述,也就是说,类其实就是定义对象的一个模板,在这个模板里面充分描述了对象所具有的属性和行为(方法)   对象:通过类具体创建出来的实体,就是对象,这些对象是可以...
1 2 3 4 5 ... 20
收藏数 533,119
精华内容 213,247
关键字:

java 面向对象