精华内容
下载资源
问答
  • 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文件中

    展开全文
  • 面向对象在生活世界

    千次阅读 2016-12-15 17:13:26
    面向对象是怎么来的,是由面向过程演变而来,所以我们要对面向过程有一个简单的了解,最常见的面向过程理解就是想用有什么就创建一个什么来用,没有太多的全面思考,而面向对象的出现就是为了修复面向过程的种种不足...

    面向对象是怎么来的,是由面向过程演变而来,所以我们要对面向过程有一个简单的了解,最常见的面向过程理解就是想用有什么就自己去制造一个什么来用,没有太多的全面思考,而面向对象的出现就是为了修复面向过程的种种不足面向对象的出现后,相比面向过程有了更多的思考,考虑了如何复用,如何扩展起来好维护,如何降低代码的耦合度,如何提高一个对象该做的事情单一职责,所以说面向对象体现出来的思想,主要包含了,高内聚低耦合,可扩展性,可复用性,等等…..,

    既然有了面向对象编程思想就会产生各种设计模式,每当理解一种设计模式,会发现自己好渺小,会发现面向对象思想,OOP, 是多么多么的强大,难道你以为面向对象思想的好处就仅仅如此吗?

    这样想的话那就错了,面向对象思想不仅仅在软件编程领域,在生活中的各个领域都是存在的,以下就用生活中的故事来举例,我们抛开程序世界,不编程,不在软件领域,我们就在生活中,既然在生活中,那生活中的各种案例就是面向对象思想的体现:


    故事一(史蒸乡找工作面试)

    人物:史蒸乡
    事物:史蒸乡毕业后,去软件公司面试,信心澎湃,面试官问了一个问题:说就以你现在来面试的情景,来描述一下你面向对象的理解?

    史蒸乡说:我先来说说面向过程吧:如果老板是一个懂编程的人,签约了一个合同,接手了一个项目,老板本人是懂编程的,老板一个人亲自来完成,结果 辛辛苦苦干了三年终于完成了,结果由于干的时间太长项目就黄了,这是面向过程的方式在完成的,发现有很多不足之处,所以就演变成了面向对象思想,例如:老板需要一个会Java编程的人,来做这个项目,所以老板需要招牌懂Java编程的人,结果我来面试了,我就是老板需要的那个对象,我这个对象就具有Java**编程属性和方法,所以老板找对象找我,就找对人了,new 史蒸乡(); 创建一个史蒸乡,史蒸乡就有编程的属性和方法,能帮老板完成工作,所以我现在来面试,面试官您就是在用面向对象**的思想招聘人。
    这里写图片描述


    故事二(做菜与带菜)

    人物:肚紫腾

    事物:肚紫腾在一家软件科技公司上班,每天都是带菜带饭来公司吃,这就需要他自己每天买菜做菜做饭,比较花费时间,有一天他在公司加班太晚,回去后就没有时间做菜做饭了,所以第二天来公司,就叫了外卖,最后他突然明白,自己买菜做菜做饭:就是属于面向过程方式,要自己亲手去做,而叫外卖就属于面向对象方式,叫外卖不需要关心外卖的菜是怎么做出来的,只需要关心 new 外卖(); 拿到外卖这个对象,就可以吃饭了,细节不需要关心。,而自己做菜做饭细节都知道。
    这里写图片描述


    故事三(汽车零部件专卖)

    人物:刘奋

    事物:刘奋在一家汽车零部件销售,这几天客户需要的配件总是很难找到,因为他是这样找的,例如:他要找方向盘配件,找完所有的方向盘配件之后,才在确定是哪个车型的,最后他仔细想了想,为何不去先确定车型在去找关于这个车型的配件呢,于是他找配件比之前要快多了,也有规律了。
    我们来分析一下:面向过程方式:(这个方向盘属于雪佛兰的,这个方向盘是属于奔驰的,这个方向盘属于一汽大众的),而最后刘奋演变成了面向对象方式:(奔驰.方向盘,一汽大众.方向盘,雪佛兰.方向盘)。
    这里写图片描述


    以上案例还有很多不足之处,只是来模拟一下场景。

    谢谢大家的观看,更多精彩技术博客,会不断的更新,请大家访问,
    刘德利CSDN博客, http://blog.csdn.net/u011967006

    展开全文
  • 面向对象

    千次阅读 2018-03-21 14:23:06
    面向对象 一封装 二多态 三继承面向对象一、封装封装是对象和类概念的主要特性。它是隐藏内部实现,稳定外部接口,可以看作是“包装”。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让...

    面向对象

    一、封装

    封装是对象和类概念的主要特性。它是隐藏内部实现,稳定外部接口,可以看作是“包装”。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
    好处:使用更简单变量更安全可以隐藏内部实现细节开发速度加快
    OC中一个类可以继承另一个类,被继承的类成为超类(superclass),继承的类是子类(childclass),可以直接拥有父类中所有非私有成员(相关实例变量)、方法。继承的实现在接口中使用符号“:”。
    举个例子:@interface Student:NSObject{}不过大家要注意的是:属性封装实例变量,方法封装具体实现代码,类封装属性和方法。子类可继承父类中的方法,还可重写父类方法。

    二、多态

    多态性(polymorphism)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
    实现多态:有二种方式,覆盖,重载。
    1)覆盖:是指子类重新定义父类的虚函数的做法。
    2)重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    三、继承

    面向对象编程(OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
    通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee是一个人,Manager也是一个人,因此这两个类都可以继承Person类。但是Leg类却不能继承Person类,因为腿并不是一个人。
    本章小结:封装可隐藏实现细节,,使代码模块化;继承可扩展已存在的代码模块(类);
    它们最终需要的结果(代码重用)。多态是为了实现另一个目的(接口重用)。多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

    似乎看起来有点复杂,不过内容其实也很简单了,希望能够对有需要的同学们有所帮助!

    展开全文
  • 面向对象总结

    千次阅读 多人点赞 2019-09-03 23:17:49
    面向对象 什么是面向对象 面向对象和面向过程对比 面向对象: 面向对象是宏观的上为用户解决功能需求,用户只需要知道怎么用对象实现功能就好了,具体底层如何实现不用操心,不过面向对象的最底层还是以面向过程的...

    面向对象

    什么是面向对象

    面向对象和面向过程对比

    面向对象:

    面向对象是宏观的上为用户解决功能需求,用户只需要知道怎么用对象实现功能就好了,具体底层如何实现不用操心,不过面向对象的最底层还是以面向过程的方式实现,但面向对象对比面向过程,减少了学习成本。

    面向过程:

    面向对象是微观下对要实现的功能进行详细设计。

    类和对象的关系

    类:

    类是对象的抽象,是将对象的相同部分提取出来

    对象:

    对象是对类的具象化的体系

    先有类还是先有对象:

    在编写过程中,是先有类,再有对象。

    类加载

    • 1:调用当前类中的静态方法时

    • 2:创建当前类的实例对象时

    构造器

    构造器

    在类中用来创建对象那个的方法称之为构造器

    1. 方法名与类名相同
    2. 没有返回值
    3. 允许方法重载
    4. 在默认情况下会自动生成无参构造器,但如果你写了构造器在不会生成。
    构造器的调用:
    1. 通过new对象,自动调用构造器
    2. 通过 this() 或 super() 分别调用当前类的构造方法和其父类的的构造方法。

    this的方法

    this.

    ​ 当前对象的属性或方法

    ​ 可省略:使用的范围内,没有同名变量时(无异议时)

    ​ 不可省略:区分同名变量,局部变量和成员变量(有异议时)

    this()

    ​ 构造器之间的互相调用

    ​ this()一定要在构造器的首行

    在这里插入图片描述

    继承

    继承的优势:
    • 在一定程度上提高了代码的复用性
    继承编写:
    • 子类 extends 父类 子类拥有父类中的所有的属性以及方法 (根据修饰确定是否能够全部继承)
    什么是继承:

    将多个类中的共性再一次抽取,抽取为一个父类。父类的作用就是用来将一些重复的内容不再多次编写(提高代码复用性)

    注意事项:
    • java中只支持单继承 一个子类有且只能有一个父类 复用性的提高是有限的
    多继承好还是单继承好
    • 多继承 :极大提高代码复用性 但是代码调用的复杂度也提升了
    • 单继承:代码调用的复杂度比较低,但是复用性比较有限
    假设在应用场景中:
    • A->B 后期随着业务不断扩展,导致A需要继承C时一般的解决办法:A->B->C
    • 但是该种解决办法随着后期业务的不断升级,导致整个继承连会变得极其复杂,既不利于后期维护以及拓展。
    • 能不能用继承就别用继承。

    super的用法

    ​ 当创建子类对象时 会先执行父类的构造器
    ​ super: 和this的用法一模一样

    super.
    • 当前对象的父类对象的
    super. 可省略的:
    • super和this的用法是重复的 都可以省略(就是没有同名时)
    super. 不可省略
    • 如果子类和父类中出现了同名变量或者是同名方法
    super()
    • 调用父类的构造器,默认情况下调用的父类的无参构造器(默认情况下每个类中都存再一个无参构造器 哪怕不写也存在)
    • 当父类中存在其他构造器时,无参构造器不存在,此时如果再子类中没有通过super()显示的指定调用的构造器会导致程序报错。
    • 在构造器中this()和super()不能同时出现,如果两个都不存在,则默认存在super()。

    在这里插入图片描述

    方法重写

    在子类中定义了和父类中同名的方法 我们将该方法称之为重写方法(覆盖)

    为什么需要重写?
    • 父类的功能不能满足子类的需求。子类在父类的基础上进行了扩展。
    如何确定一个方法是重写方法?
    • 在子类的方法上加入@Overried 注解 如果不报错 证明是重写
    重写的前提:
    • 一定要发生继承关系。并且子类的方法名和父类的方法名同名
    • 参数列表要一样
    • 返回类型要一样

    Object

    Object: 是所有类的根基类 超类 父类
    • 当一个类没有显式的继承关系的时候,默认情况下他的父类都是Object
    Object:
    • toString : 输出对象 全限定名(包名.类名)@16进制hash值
    • 输出一个对象的时候默认情况下会调用当前对象的toString
    • getClass:获取当前类的Class对象 反射
    • == 比较基本数据类型比较的是值 比较引用类型比较的是地址
    • equals:如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;诸如String、Date等类对equals方法进行了重写的话,比较的是所是否是同一类型的对象(不一定是同一对象),内容是否相等。
    • Object中的比较是通过==比较的

    封装

    权限修饰符

    权限修饰符 本类 同包下子类 同包下无关类 异包子类 异包下无关类
    public
    protected ×
    默认的 × ×
    private × × × ×

    注意:

    1. 对于class的权限修饰只可以用publicdefault(默认的)

    状态修饰符

    static(静态的)

    1. 可以修饰成员变量,成员方法,不能修饰类(除了内部类),不能修饰构造方法。
      被修饰的变量,方法可以通过类名进行调用。
    2. 可以修饰静态代码块 静态方法 静态变量 静态常量
    3. 静态方法不可以直接调用调用非静态方法 可以创建对象调用
    4. 非静态方法可以调用静态方法
    5. 静态内容共享的 被所有类和对象共享 修改之后都可见
    6. 静态内容可以通过类名. 或者是对象.
    7. 类加载就会被加载 只被加载一次
    8. 不可以使用this.和super.
    9. 静态内容调用时会导致类被加载

    final(最终的)

    可以修饰类,成员变量,成员方法,不能修饰构造方法。
    修饰的类不能被继承,被修饰的变量是常量,被修饰的方法不能被继承。

    抽象修饰符

    abstract(抽象的)

    不能修饰构造方法,不能修饰成员变量,可以修饰类(接口),成员方法

    被修饰的类(接口)不能被实例化,只能被继承,被修饰的成员方法在子类中必须被重写

    总结

    修饰符 成员变量 成员方法 接口 构造方法
    public
    protected x(外部类) ×
    default(默认的)
    private x(外部类) ×
    static(静态的) √(内部类) × ×
    final(最终的) × ×
    abstract(抽象的) × ×

    单例模式

    单例模式

    一个类只能产生一个实例对象

    如何编写

    • 1:构造器私有
    • 2:对外提供过去对象的方法
    • 3:声明一个static的成员变量 类加载的时候创建当前单例对象
    • 4:在获取对象方法中返回成员变量的值
    饿汉式
    优缺点分析
    • 优点: 天然线程安全
    • 缺点: 不能做到延迟加载
    public class Single {
        // 声明一个Single对象
        public static Single single = new Single();
        //1:将构造器私有
        private Single() {
        }
        /**
        * public : 外界一定是通过该方法获取当前类的实例对象 所以对外一定要可见
        * static : 构造器都私有了 外部肯定无法获取到当前类的实例对象 所以只能用static修饰 属于类的 	可以通
        过类名调用
        * 不加static要通过对象调用 对象没有
        * 返回值 : 当前类的实例
        *
        */
        public static Single getInstance() {
        	return single;
        }
        public static void add() {
        }
    }
    
    懒汉式
    优缺点分析
    • 优点: 能够做到延迟加载
    • 缺点: 但是线程不安全
    public class Lazy {
        private static Lazy lazy = null;
        private Lazy() {
        }
        public static Lazy getInstance() {
        	if(lazy==null) {
        		lazy = new Lazy();
        	}
        	return lazy;
    	}
    }
    

    多态

    多种形态

    多态前提

    1. 继承关系
    2. 父类变量指向了子类对象
    3. 一定要有方法的重写

    类的加载顺序

    {} 代码块:
    局部代码块:
    • 声明在方法中的代码块
    • 缩减局部变量的生命周期 提高内存是使用率
    成员代码块:
    • 声明在方法外 类中的代码块 初始化块

    • 初始化块在类加载的时候是不会执行的

    • 在创建对象之前会被调用(对于对象中的一些初始进行初始化操作)

    静态代码块:
    • 声明在类中 方法外 且使用static修饰
    • 类加载的时候就会被加载 并且只加载1次 静态内容
    类中的执行顺序:
    • 1:首先执行静态内容(加载) 静态代码块
    • 2:初始化块
    • 3:构造器

    类型转换

    引用类型也有类型转换:
    自动转换
    • 父类型 变量名 = 子类对象;(new 子类对象 或 子类对象的变量)
    强制转换
    • 子类型 变量名 = (子类型)父类变量;
    • 事先确定了父类变量中实际存储的对象是什么类型,相同类型才能转 ,不然报 ClassCastException 类型转换异常错误

    final

    • final修饰的变量称之为最终常量 在程序运行期间其值不可发生改变
    • final修饰的类不可以被继承:太监类
    • final修饰的方法不可以被重写

    final修饰静态常量经过方法

    • final 修饰的基本数据类型变量 无法进行修改其值。
    • final 修饰的引用类型的变量 只保证地址不变 对象中的内容可以发生改变

    final修饰的静态常量不会导致类加载

    • 静态成员常量不会导致类加载
    • 静态成员常量的值在加载前无法确定 那么会导致类加载(如下代码)
    public class Test02 {
        final static int num  = (int)(Math.random()*33);//10;
        static {
        	System.out.println("我是静态代码块");
        }
        public static void main(String[] args) {
       		System.out.println(Test02.num);
        }
    }
    
    public class Test03 {
        public static void main(String[] args) {
        	System.out.println(Test02.num);
    	}
    }
    

    类的加载顺序

    分析:

    1. 先加载静态内容 -> 静态代码块

    2. 由于父子关系 所以子类加载之前需要先加载父类

    3. 执行的父类的初始化块和构造器(因为子类构造器中有一个super)

    4. 然后执行子类的初始化块和构造器

    加载顺序:

    • 父类的静态代码块
    • 子类的静态代码块
    • 父类的初始化块
    • 父类的构造器
    • 子类的初始化块
    • 子类的构造器

    在这里插入图片描述

    abstract 抽象类

    抽象类基础概念

    ​ 1: 父类中定义的方法不需要具体的实现步骤 子类都不按照父类的做

    ​ 2: 父类中定义这个方法的目的是告诉子类 一定要保证存在该方法

    对于类的要求:

    ​ 1:父类中不需要定义方法的实现步骤

    ​ 2:子类必须要重写

    抽象类:

    ​ 包含了抽象方法的的类称之为抽象类。

    ​ 被abstract修饰的类称之为抽象了

    抽象方法:

    ​ 只要方法的声明,没有方法体。 通过abstract修饰的方法称之为抽象方法

    为什么需要抽象类?

    ​ 避免子类的随意设计 提高了代码可读性 提高了子类的健壮性

    深入理解抽象类

    1:抽象类中只能包含抽象方法吗?

    ​ 既可以定义抽象方法也可以定义普通方法

    2:是否可以定义构造器

    ​ 抽象类可以存在构造器但是无法实例化

    ​ 抽象类中的构造器是给子类准备的

    ​ 抽象类就是用来被继承的 抽象方法就是被重写的

    3:子类继承了抽象了之后一定要重写所有的抽象方法

    接口

    接口是一个规范 是一套标准 比抽象类还抽象

    接口的简单定义

    • 修饰符 interface 接口名{}

    • 接口中的变量都是公开的 静态的最终常量值 默认情况下变量都是public static final修饰

    • 接口中可以定义静态方法(不建议1.8)

    • 接口中定义的对象方法都是抽象方法 接口中的方法默认就是通过abstract修饰的

    • 接口中的默认方法从1.8之后才开始被使用 允许在接口中定义default方法 而且存在方法体

    深入理解接口

    接口深入:

    • 类和接口直接通过implements 发生关系 类实现接口
    • 类必须要实现接口中的所有抽象方法
    • 一个类可以实现多个接口 类名 implements 接口1,接口2。。。。。
    • 一个类实现了接口之后 要将当前接口以及接口的父接口中的所有抽象方法全部重写
    • 接口可以多继承
    • 接口无法实例化
    • 接口没有构造器
    • 接口中也可以使用多态

    Tips:

    接口就是一套规则,用来定义具体要做哪些事情,但是所有事情的具体实现都会延迟到实现类中完成。接口只需要定义has-a的关系,如果你是什么,则你具备了什么能力。

    equals方法

    equals方法就是用来比较两个对象是否相等的,默认Object的equals方法比较是两个对象的地址。

    • java.lang.NullPointerException 空指针异常 对象为null

    • ClassCastException 类型转换异常

    • null可以强转为任意类型

    • null用instanceof 跟任何类型比较时都是false

    内部类

    普通内部类

    外部类中如何使用内部类的属性以及方法

    在外部类中创建内部类对象,就可以调用内部类功能、属性

    内部类中使用外部类的属性以及方法

    可以直接使用

    其它类中调用内部类中的属性和方法:

    第一种:

    ​ a:导包 包名.外部类.内部类

    ​ b:内部类类型 变量名= new 外部类对象().new 内部类对象

    ​ Inner01 in = new Outer01().new Inner01();

    第二种:

    ​ 外部类.内部类 变量名= new 外部类对象().new 内部类对象

    ​ Outer01.Inner01 in = new Outer01().new Inner01();

    静态内部类

    静态内部类:

    通过static修饰的内部类称之为静态内部类

    ​ a:外部类中如何使用静态内部类的属性以及方法

    ​ 创建对象,调用对象的属性以及方法

    ​ b:静态内部类中使用外部类的属性以及方法

    ​ 创建外部类的对象 调用对象的属性以及方法

    ​ c:其它类中调用内部类中的属性和方法:

    ​ 第一种:

    ​ a:导包 包名.外部类.内部类

    ​ b:内部类类型 变量名= new 外部类. 内部类对象()

    ​ Inner01 in = new Outer02.Inner01();

    ​ 第二种:

    ​ 外部类.内部类 变量名= new 外部类. 内部类对象()

    ​ Outer01.Inner01 in =new Outer02.Inner01();

    编写静态内部类完成单例模式创建

    线程安全问题: 天然线程安全

    延迟加载: 能做到延迟加载

    类何时被加载: 调用静态内容 创建对象

    类加载的时候 首先将静态内容加载大内存中

    public class Single {  
          
        static class SingleHolder{  
            private static Single single = new Single();  
        }  
          
        private Single() {  
              
        }  
          
        public static Single getInstance() {  
            return SingleHolder.single;  
        }  
          
        public static void add() {  
              
        }  
      
        public static void main(String[] args) {  
            Single.add();  
            Single s1 = getInstance();  
        }  
    }  
    

    局部内部类

    局部内部类,这种内部类是局部的,实际上和局部变量有点类似,是定义在方法中的,不过在实际开发中,对于局部内部类的使用是很少的。

    匿名内部类

    通过匿名内部类可以产生一个接口/抽象了的 实现类对象/子类对象

    数组

    什么是数组:

    一组数 (数据) 的集合

    官方定义:

    ​ 在内存中 通过连续的存储单元 存储相同数据类型的 有序集合

    如何定义数组:

    ​ 数据类型[] 变量名;

    如何初始化数组:

    ​ 变量名 = new 数据类型[数组的长度];

    ​ arrs = new int[10]

    获取数组中的元素

    ​ 数组变量[索引] (索引从0开始 到 长度-1 结束)

    获取数组中的长度:

    ​ 数组变量.length

    编写ArrayList类

    package com.mage.arrays;
    
    public class ArrayList {
    	private Object[] arrs;
    	private int size; //数组实际存储元素个数
    	private int capacity;//底层arrs数组的长度,也就是开的内存空间大小
    	private static final int DEFAULT_CAPACITY = 10;
    	
    	public ArrayList() {
    		this(DEFAULT_CAPACITY);
    	}
    	
    	public ArrayList(int capacity) {
    		arrs = new Object[capacity];
    		this.capacity = capacity;
    	}
    	
    	/**
    	 * @return 返回数组实际存储的元素个数
    	 */
    	public int size(){
    		return this.size;
    	}
    	
    	/**
    	 * @return 返回当前ArrayList底层数组的容量
    	 */
    	public int opacity() {
    		return arrs.length;
    	}
    	
    	/**
    	 * @return 返回当前数组是否为null
    	 */
    	public boolean isEmpty() {
    		return this.size==0;
    	}
    	
    	/**
    	 * 	 添加元素到指定的位置上
    	 * @param value 添加的元素
    	 * @param index 添加的位置
    	 */
    	public void add(Object value,int index) {
    		if(index<0||index>size) {
    			System.out.println("错误参数:index");
    			return;
    		}
    		//存满了
    		if(size==arrs.length) {
    			resize(size*2);
    		}
    		
    		for(int i=size-1;i>=index;i--) {
    			arrs[i+1]=arrs[i];
    		}
    		arrs[index]=value;
    		size++;
    	}
    	
    	/**
    	 *  	添加首元素
    	 * @param value 添加的元素值
    	 */
    	public void addFirst(Object value) {
    		add(value,0);
    	}
    	
    	
    	/**
    	 *  	添加尾元素
    	 * @param value
    	 */
    	public void addLast(Object value) {
    		add(value,size);
    	}
    	/**
    	 *  	查询指定元素在当前数组中的索引位置(只找一个)  
    	 * @param value
    	 * @return 查找的元素的索引 如果不存在返回-1
    	 */
    	public int getIndexByValue(Object value) {
    		for(int i=0;i<size;i++) {
    			if(arrs[i]==value) {
    				return i;
    			}
    		}
    		return -1;
    	}
    	
    	/**
    	 *      返回指定索引位置上的元素
    	 * @param index 索引
    	 * @return 元素    如果返回null代表当前数组的入参有误
    	 */
    	public Object get(int index) {
    		if(index<0||index>=size) {
    			System.out.println("参数有误:index");
    			return null;
    		}
    		return this.arrs[index];
    		
    	}
    	
    	
    	/**
    	 *       	修改数组中指定位置上的元素
    	 * @param index  指定的索引
    	 * @param value  修改之后的值
    	 * @return  修改之前的值 如果索引有问题 返回null
    	 */
    	public Object set(int index,Object value) {
    		if(get(index)==null) {
    			return null;
    		}
    		Object oldValue=arrs[index];
    		arrs[index]=value;
    		return oldValue;
    		
    	}
    	
    	/**
    	 * 	根据索引删除元素
    	 * @param index 索引
    	 * @return 删除的元素   如果索引有误 返回null
    	 */
    	
    	public Object remove(int index) {
    		if(get(index)==null) {
    			return null;
    		}
    		Object oldValue = arrs[index];
    		for(int i=index;i<size-1;i++) {
    			arrs[i]=arrs[i+1];
    		}
    		size--;
    		arrs[size]=null;
    		if(size==arrs.length/4&&arrs.length/2>0) {
    			resize(arrs.length/2);
    		}
    		return oldValue;
    		
    	}
    	
    	
    	/**
    	 *  	删除第一个元素
    	 * @return
    	 */
    	public Object removeFirst() {
    		return remove(0);
    	}
    	
    	/**
    	 *  	删除最后元素
    	 * @return
    	 */
    	public Object removeLast() {
    		return remove(size-1);
    	}
    	
    	/**
    	 * 数组扩容操作
    	 * @param capacity 新数组的容量
    	 */
    	private void resize(int capacity) {
    		Object[] newArrs = new Object[capacity];
    		copyOf(arrs,newArrs);
    		arrs = newArrs;
    	}
    	
    	/**
    	 *  	数组复制
    	 * @param src 源数组
    	 * @param dest 目标数组
    	 */
    	private void copyOf(Object[] src,Object[] dest) {
    		for(int i=0;i<this.size;i++) {
    			dest[i]=src[i];
    		}
    	}
    
    	/**
    	 * 获取当前ArrayList的内容
    	 */
    	public String toString() {
    		StringBuffer sb = new StringBuffer();
    		sb.append("size:"+this.size+"\topacity:"+this.arrs.length+"\t");
    		sb.append("[");
    		for(int i = 0;i<size;i++) {
    			sb.append(arrs[i]);
    			if(i!=size-1) {
    				sb.append(",");
    			}
    		}
    		sb.append("]");
    		return sb.toString();
    	}
    }
    
    

    测试类

    package com.mage.arrays;
    
    public class TestArrayList {
    	public static void main(String[] args) {
    		
    		// ArrayList存储数据的对象
    		ArrayList arrayList = new ArrayList();// size = 0 capacity 10   
    		
    		//测试增加不同类型值
    		arrayList.addFirst('a');
    		arrayList.addFirst("哈哈");
    		arrayList.addFirst(44);
    		System.out.println(arrayList.toString());
    		
    		//测试在最后位置加值
    		arrayList.addLast(33);
    		System.out.println(arrayList.toString());
    		
    		//测试元素加满了,内存扩容。
    		arrayList.addLast(33);
    		arrayList.addLast(33);
    		arrayList.addLast(33);
    		arrayList.addLast(33);
    		arrayList.addLast(33);
    		arrayList.addLast(44);
    		arrayList.addLast(55);
    		System.out.println(arrayList.toString());
    		
    		//测试移除增加元素会撤回扩容嘛
    		arrayList.remove(0);
    		System.out.println(arrayList.toString());
    		
    		//测试元素个数为原来的四分之一会压缩一半内存嘛
    		arrayList.remove(0);
    		arrayList.remove(0);
    		arrayList.remove(0);
    		arrayList.remove(0);
    		arrayList.remove(0);
    		System.out.println(arrayList.toString());
    		
    		//测试修改
    		arrayList.set(1, "被修改了");
    		System.out.println(arrayList.toString());
    	}
    }
    
    

    在这里插入图片描述

    排序

    冒泡排序

    第一种
    	public static void bubbleSort01(int[] arrs) {
    		System.out.println("原数组:"+toString(arrs));
    		for(int i=0;i<arrs.length-1;i++) {//控制趟数
    			System.out.println("第"+(i+1)+"趟");
    			for(int j=0;j<arrs.length-1;j++) {//次数
    				if(arrs[j]>arrs[j+1]) {
    					int temp = arrs[j];
    					arrs[j] = arrs[j+1];
    					arrs[j+1] = temp;
    				}
    				System.out.println("第"+(j+1)+"次:"+toString(arrs));
    			}
    		}
    	}
    

    在这里插入图片描述
    在这里插入图片描述

    第二种
    public static void bubbleSort02(int[] arrs) {
    		System.out.println("原数组:"+toString(arrs));
    		for(int i=0;i<arrs.length-1;i++) {//控制趟数
    			System.out.println("第"+(i+1)+"趟");
    			for(int j=0;j<arrs.length-1-i;j++) {//次数
    				if(arrs[j]>arrs[j+1]) {
    					int temp = arrs[j];
    					arrs[j] = arrs[j+1];
    					arrs[j+1] = temp;
    				}
    				System.out.println("第"+(j+1)+"次:"+toString(arrs));
    			}
    		}
    		
    	}
    

    在这里插入图片描述
    在这里插入图片描述

    第三种
    public static void bubbleSort03(int[] arrs) {
    		System.out.println("原数组:"+toString(arrs));
    		for(int i=0;i<arrs.length-1;i++) {//控制趟数
    			//声明一个是否排好序
    			boolean isSorted = true;
    			System.out.println("第"+(i+1)+"趟");
    			for(int j=0;j<arrs.length-1-i;j++) {//次数
    				if(arrs[j]>arrs[j+1]) {
    					isSorted = false;
    					int temp = arrs[j];
    					arrs[j] = arrs[j+1];
    					arrs[j+1] = temp;
    				}
    				System.out.println("第"+(j+1)+"次:"+toString(arrs));
    			}
    			//判定
    			if(isSorted) {
    				break;
    			}
    		}
    	}
    	
    

    在这里插入图片描述

    第四种
    public static void bubbleSort04(int[] arrs) {
    		System.out.println("原数组:"+toString(arrs));
    		//基准点
    		int index = arrs.length-1;
    		for(int i=0;i<arrs.length-1;i++) {//控制趟数
    			//声明一个是否排好序
    			boolean isSorted = true;
    			
    			int tmp = 0; 
    			System.out.println("第"+(i+1)+"趟");
    			for(int j=0;j<index;j++) {//次数
    				if(arrs[j]>arrs[j+1]) {
    					isSorted = false;
    					int temp = arrs[j];
    					arrs[j] = arrs[j+1];
    					arrs[j+1] = temp;
    					tmp = j;
    				}
    				System.out.println("第"+(j+1)+"次:"+toString(arrs));
    			}
    			index = tmp;
    			//判定
    			if(isSorted) {
    				break;
    			}
    		}
    	}
    

    在这里插入图片描述

    快速查找

    	public static void SelsctSort(int[] arrs) {
    		
    		for(int i=0;i<arrs.length-1;i++) {
    			int min = i;
    			for(int j=i+1;j<arrs.length;j++) {
    				if(arrs[min]>arrs[j]) {
    					min=j;
    				}
    			}
    			if(arrs[i]!=arrs[min]) {
    				int tmp = arrs[min];
    				arrs[min] = arrs[i];
    				arrs[i]=tmp;
    			}
    		}
    			
    	}
    

    二分查找

    	public static int bSearch(int[] arrs,int value) {
    		//声明索引
    		int max = arrs.length-1;
    		int min = 0;
    		int mid = 0;
    		//循环判定
    		while(min<=max) {
    			mid = (max+min)/2;
    			if(arrs[mid]>value) {
    				max = mid-1;
    			}else if(arrs[mid]<value) {
    				min = mid+1;
    			}else {
    				return mid;
    			}
    		}
    		return -1;
    	}
    

    异常

    try-catch

    语法结构:

    ​ try{

    ​ //有可能出现异常的代码块

    ​ }catch(声明异常){

    ​ 异常解决办法

    ​ }

    执行顺序:

    1:执行try块中的内容

    2:如果try块中内容出现异常,执行catch块

    3:匹配catch中声明的异常信息 ,如果匹配上,则执行catch中的代码

    4:继续执行后续代码

    5:如果catch中的异常信息没有匹配到 那么此时交由jvm处理该异常

    注意:

    catch:中的类型一定要能够捕获到try快中实际出现的异常信息 如果忘记了具体的异常信息可以通过使用Exception去捕获异常信息,不要再catch块中做业务逻辑判定

    	try {
            System.out.println(1/0);
        }catch(ArithmeticException e) {
            System.out.println("输入的数据有误。。。。。");
        }
    	System.out.println("我是try-catch外的代码");
    

    在这里插入图片描述

    try多层catch

    语法结构:

    ​ try{

    ​ //可能出现异常的代码段

    ​ }catch(异常1){

    ​ //异常 的解决办法

    ​ }catch(异常2){

    ​ //异常 的解决办法

    ​ }…{

    ​ }

    try多层catch执行顺序:

    1:执行try块 如果出现异常 new出当前异常对象

    2:逐一匹配catch中的异常内容

    3:如果匹配上 执行对应的catch中的代码 整个try-catch执行结束

    4:如果匹配上 jvm去解决当前异常信息

    注意事项:

    1:在多重catch中一般情况下会在最后一个catch语句中编写exception 用来捕获可能未被识别的异常信息

    2:不要再第一个catch中编写父类异常 屏蔽所有子类异常

    异常对象的常见方法:

    toString: 当前异常类型以及原因描述

    在这里插入图片描述

    printStackTrace:打印堆栈信息 异常类型以及原因描述 异常位置

    在这里插入图片描述

    getMessage:异常原因

    在这里插入图片描述

    	Scanner input = new Scanner(System.in);
        try {
            System.out.println("请输入第一个数:");
            int num1 = input.nextInt();
            System.out.println("请输入第二个数:");
            int num2 = input.nextInt();
    
            int result = num1/num2;
            System.out.println(num1+"/"+num2+"="+result);
        }catch(ArithmeticException e) {
            System.out.println("除数不能为0");
            //System.out.println(e.toString());
            //e.printStackTrace();
            //System.out.println(e.getMessage());
        }catch(InputMismatchException e) {
            System.out.println("用户输入有误");
        }catch(Exception e) {
            System.out.println("网络加载问题!!!");
        }
    
        System.out.println("后续代码。。。");
    

    在这里插入图片描述

    try-catch-finally

    语法结构:

    ​ try{

    ​ //可能出现异常的代码

    ​ }catch(异常1){

    ​ //处理办法

    ​ }…{

    ​ }finally{

    ​ //代码块

    ​ //关闭资源的代码

    ​ }

    测试如何让finally不执行

    retrun 语句不会让finally不执行

    finally先于return语句执行

    代码中存在System.exit() 可以让finally不执行 但是一般不这么干

    执行顺序:

    1:执行try块 如果出现异常 new出当前异常对象

    2:逐一匹配catch中的异常内容

    3:如果匹配上 执行对应的catch中的代码

    4:如果未匹配上 jvm去解决当前异常信息

    5:不论是否匹配成功 都会执行finally中内容

    注意:

    finally中一般情况下编写关闭资源的操作

    jdk1.7之后对于try-catch-finally的改变

    可以通过对于流、网络连接对象的创建声明在try的()中,后续无序通过使用finally显式的关闭资源

    ​ try(资源1;资源2.。。。){

    ​ //可能出现的异常信息

    ​ }catch(异常1){

    ​ //解决办法

    ​ }。。。。。{

    ​ }

    	Scanner input = new Scanner(System.in);
        try {
            System.out.println("请输入第一个数:");
            int num1 = input.nextInt();
            System.out.println("请输入第二个数:");
            int num2 = input.nextInt();
            int result = num1/num2;
            System.out.println(num1+"/"+num2+"="+result);
            input.close();
        }catch(InputMismatchException e) {
            System.out.println("输入有误");
        }catch(ArithmeticException e) {
            System.out.println("除数不能为0");
        }catch(Exception e) {
            System.out.println("网络加载有误。。");
        }finally {
            // 一定会被执行
            System.out.println("我是finally块中的代码");
            input.close();
        }
        System.out.println("后续代码。。。。");
    

    在这里插入图片描述

    throw-throws

    throw

    1:throw 声明当前代码块中可能存在的异常信息 并且将当前异常信息抛出给调用者。

    对于调用者而言 通过try-catch捕获异常

    如果调用者也不管当前异常交由jvm解决

    2:throw会导致当前程序中断掉 后续代码不执行

    throws

    • 在方法外对外抛出某个异常,调用者解决当前异常。main方法中对外抛出的异常全部都交由jvm做。

    • throws抛出异常 是异常解决的一种办法定义在方法的外部 形式参数后 可以抛出多个异常通过","分割

    • 一般会将throws和throw在一起使用,throw 声明的异常是检查时异常需要和throws一起使用,但是throws可以单独使用

    public class Test09 {
    	public static void main(String[] args) {
            //创建student对象
    		Student stu1 = new Student();
    		stu1.setName("李四");
    		try {
    			stu1.setAge(-1);
    		} catch (RuntimeException e) {
    			System.out.println("参数有误");
    		}
    		System.out.println(stu1);
    		try {
    			info();
    		}catch(FileNotFoundException e){
    			System.out.println("找不到文件!");
    		}
    	}
    	
    	public static void info() throws FileNotFoundException{
    		//try {
    			new FileInputStream(new File(""));
    		/*}catch(FileNotFoundException e) {
    			System.out.println(e);
    		}*/
    	}
    }
    
    class Student{
    	
    	private String name;
    	private int age;
    	
    	public Student() {
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getAge() {
    		return age;
    	}
    	
    	/**
    	 * 
    	 * @param age
    	 * @throws RuntimeException 当前方法对外会抛出异常 如果参数有误
    	 */
    	public void setAge(int age)/* throws FileNotFoundException */{
    		if(age<0||age>150) {
    			throw new RuntimeException();//throw new FileNotFoundException
    		}
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + "]";
    	}
    	
    

    在这里插入图片描述

    自定义异常

    1:jdk中提供的异常信息不满足目前的使用

    2:如何自定义异常:

    ​ a:声明一个自定义异常类

    ​ b:继承Exception

    ​ c:编写两个构造器 一个空 一个带字符串的构造器

    使用自定义异常

    class User{
    	private int age;
    	public User() {
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) throws AgeException{
    		if(age<0||age>150) {
    			throw new AgeException("年龄输入有误");
    		}
    		this.age = age;
    	}
    	@Override
    	public String toString() {
    		return "User [age=" + age + "]";
    	}
    }
    

    自定义异常类

    public class AgeException extends Exception{
    	public AgeException() {
    	}
    	public AgeException(String msg) {
    		super(msg);
    	}
    }
    
    

    测试类

    public class Test10 {
    	public static void main(String[] args){
    		User u = new User();
    		try {
    			u.setAge(-1);
    		}catch(AgeException e) {
    			System.out.println(e.getMessage());
    		}
    		System.out.println(u);
    		
    	}
    }
    

    在这里插入图片描述

    展开全文
  • 深入理解Java面向对象

    万次阅读 多人点赞 2016-08-03 09:13:10
    深入理解Java面向对象
  • 面向对象基本概念

    万次阅读 多人点赞 2019-02-06 21:56:15
    面向对象就是:把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象...
  • 面向对象编程以及Python面向对象

    千次阅读 2015-11-04 22:59:14
    一、面向对象编程 面向对象编程(OOP)作为一种方式,相对于面向过程更加的贴近人的思维习惯。 面向对象是将一个整体问题划分...个人理解面向对象是一个总分的结构,从一个模块提取出共同的部分就是类,这个类有
  • 深入理解Python面向对象

    万次阅读 多人点赞 2017-06-12 06:42:41
    面向过程与面向对象的对比 id、type和value的讲解 类和对象的概念 初始化构造函数__init__的作用 self关键字的使用 继承的概念 组合的概念 接口的概念 抽象类的概念 1、面向过程与面向对象的对比面向过程的程序设计...
  • Kotlin面向对象(一)

    千次阅读 2017-05-24 16:56:36
    面向对象的含义大家应该并陌生,通过将事物抽象成对象,大大简化了程序的开发难度。我们常用的Java、Python、C++都属于面向对象的编程语言。Kotlin和java很相似,也是一种面向对象的语言。作为Kotlin最重要的一...
  • 简述Java面向对象

    千次阅读 2017-07-02 21:33:37
    Java面向对象思想是Java程序设计的核心理论与方式,那么什么是面向对象面向对象与面向过程有什么区别?祥见本文。
  • Python中面向对象(学习笔记)

    万次阅读 多人点赞 2020-06-15 09:29:18
    文章目录一、面向过程与面向对象简述面向过程编程面向对象编程二、类和对象类对象(实例)类和对象的关系三、类的定义四、类的成员类属性实例函数(对象函数,成员函数)实例函数和普通函数的区别关于self创建对象...
  • java基础知识点04_面向对象

    万次阅读 2020-08-16 23:24:11
    java基础知识点04_面向对象 面向对象编程OOP 的本质就是:以类的方式组织代码,以对象的形式封装数据 面向对象三大特性:封装、继承、多态 对象是具体的,类是对对象的抽象,描述对象的共同特征,是对象创建的模板...
  • C++面向对象程序设计 面向对象编程

    万次阅读 2018-09-12 22:39:50
    1.1 面向过程的编程风格与面向对象的编程风格 C语言是面向过程语言,也称为命令型语言,面向过程通常采用自顶向下设计,问题复杂时不断运用自顶向下设计(即函数分解法)直到容易处理。自顶向下设计优点是直观有...
  • Java面向对象

    千次阅读 2016-05-29 14:34:23
    对外部可见,可以保护程序的某些内容。 继承性:扩展功能。 多态:方法的重载,对象的多态性。 - 类与对象的关系: 面向对象的核心组成部分,就是类与对象。 类是对某一事物的描述,是抽象的,概念上的定义; ...
  • python 面向对象全面详解

    万次阅读 多人点赞 2019-01-12 14:25:23
    一、对面向对象的理解 1、面向对象的编程---object oriented programming 2、python面向对象的重要术语: 3、函数和面向对象编程的区别 二、封装、继承、多态 1、封装(Encapsulation) 2、继承(Inheritance...
  • Java中面向对象的编程概念

    千次阅读 2016-06-07 21:21:53
    面向对象的编程概念(1)-java学习之路一、对象 在面向对象的编程设计,对象当然是最基本的概念。知道何为对象,怎么面向对象呢。  对象一词最早来源于哲学,讲到“一切皆对象”,这里讨论哲学的问题了。...
  • 面向对象:就是找个工具,帮我完成一项工作,对象就像一个工具一样,每个工具都可以帮我们实现某个功能,比如汽车可以实现运输,我们只需要学会如何去开动汽车,而需要知道汽车是如何实现开动的。面向对象的好处:...
  • C++ 面向对象思想

    千次阅读 2018-10-31 16:53:14
    C++面向对象 面向对象设计思维 C语言面向过程语言 以函数为中心,数据属于丛属位置... 注意:项目开发过程面向对象和面向过程会同时使用, 通过面向对象把一个项目细分,分工合作,最后需要 通过面向过程组...
  • JavaScript 面向对象编程

    千次阅读 2016-04-26 23:13:09
    为了说明 JavaScript 是一门彻底的面向对象的语言, 首先有必要从面向对象的概念着手 , 探讨一下面向对象中的几个概念:1. 一切事物皆对象 2. 对象具有封装和继承特性 3. 对象与对象之间使用消息通信,各自存在信息...
  • 万字长文深度剖析面向对象的javascript

    万次阅读 热门讨论 2020-12-02 09:46:56
    本将会深入讲解面向对象在javascript的应用,并详细介绍三种对象的生成方式:构造函数,原型链,类。
  • 【Java面向对象学习】一张图搞定Java面向对象

    千次阅读 多人点赞 2017-09-13 07:56:41
    【Java面向对象学习】一张图搞定Java面向对象,分享一些归纳总结后的技术干货,一张图能搞定的事,绝对长篇大论。 大家都知道,Java面向对象是Java的一个核心,也是初学者的一个难点,所以我们从这里开始,Let...
  • js面向对象理解

    万次阅读 多人点赞 2019-05-13 21:25:29
    面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,ECMAScript 没有类的概念,因此它的对象也与基于类的语言的对象有所不同。 js(如果没有作特殊说明,本文...
  • (本教程适用于已有一定matlab基础及面向对象程序设计基础,但没有matlab面向对象基础的读者参考)不同于面向过程的语言,面向对象语言的核心在于如何编写一个合适的类(class),从而将我们要处理的对象赋予明确的属性以及...
  • C++面向对象基础

    万次阅读 多人点赞 2018-05-20 12:40:59
    面向对象基础面向对象三大特性封装性:数据和代码捆绑一起,避免外界干扰和确定性访问。封装可以使得代码模块化。优点:确保用户代码不会无意间破坏封装对象的状态被封装的类的具体实现细节可以随时改变,而无须...
  • Java面向对象总结篇

    千次阅读 多人点赞 2019-01-17 15:48:33
    面向对象在百度百科是这样解释的:“面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物”。说的好像很流弊的样子,看看就行。 说道面向对象,大家肯定会想到面向对象的三大基本...
  • 如何理解Java面向对象

    万次阅读 多人点赞 2018-08-01 14:28:30
    &nbsp;&nbsp;...我的理解是:面向对象是向现实世界模型的自然延伸,这是一种”万物皆对象”的编程思想。现实生活的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。 &
  • 面向对象思想————不看后悔! ————转载自:https://zhidao.baidu.com/question/751474259711412524.html 前言: 整理这份资料的目的是为了帮助朋友们能够更直观的理解面向对象的编程。让后来者能够少走一些...
  • 面向对象与面向过程理解

    千次阅读 2016-10-31 20:21:08
    面向对象与面向过程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 243,709
精华内容 97,483
关键字:

在面向对象中不属于对象