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

    展开全文
  • 1. 面向对象程序设计的基本概念 Java是一种面向对象的编程语言。面向对象编程,英文是Object-Oriented Programming,简称OOP。 那什么是面向对象编程? 面向对象其实是现实世界模型的自然延伸。可以将现实世界中的...

    原文链接:面向对象设计—类和对象**

    1. 面向对象程序设计的基本概念

    Java是一种面向对象的编程语言。面向对象编程,英文是Object-Oriented Programming,简称OOP。

    那什么是面向对象编程?

    面向对象其实是现实世界模型的自然延伸。可以将现实世界中的任何实体都看做是对象,对象之间通过消息相互作用。另外,现实世界中的任何实体都可归属于某类事物,任何对象都是某一类事物的实例。如果说传统的过程式编程语言是以过程为中心、以算法为驱动的话,面向对象的编程语言则是以对象为中心、以消息为驱动。用公式表示,过程式编程语言为:程序=算法+数据,面向对象编程语言为:程序=对象+消息。

    所有的面向对象编程语言都支持3个概念,即封装、多态性和继承。现实世界中的对象均有属性和行为,映射到计算机程序上,属性则表示对象的数据,行为表示对象的方法(其作用是处理数据或同外界交互)。

    所谓封装,就是用一个自主式框架把对象的数据和方法连在一起形成一个整体。可以说,对象是支持封装的手段,是封装的基本单位。类描述了一组有相同特性(属性)和相同行为(方法)的对象。在程序中,类实际上就是数据类型,例如整数、小数等。整数也有一组特性和行为。面向过程的语言与面向对象的语言的区别就在于,面向过程的语言不允许程序员自己定义数据类型,而只能使用程序中内置的数据类型。而为了模拟真实世界,为了更好地解决问题,我们往往需要创建解决问题所必需的数据类型。

    接下来我们讨论以下几个词的具体概念:

    • 对象
    • 封装性
    • 继承性
    • 多态性

    2. 类与对象

    对象

    现在举个例子,比如现在由一辆汽车,汽车的外观是红色,并由发动机、方向盘、轮胎等组成,并且汽车可以行驶在马路上。如果将汽车搬到Java语言中,汽车就是一个对象,外观、发动机、方向盘、轮胎都属于汽车对象的属性,行驶的动作就是汽车对象的方法。

    综上所述,对象的特征分为静态特征动态特征两种。静态特征指对象的外观、性质、属性等,动态特征指对象具有的功能、行为等。

    人们将对象的静态特征抽象为属性,用数据来描述,在Java语言中称之为变量;人们将对象的动态特征抽象为行为,用一组代码来表示,完成对数据的操作,在Java语言中称之为方法。一个对象由一组属性和一组对属性进行操作的方法构成。

    将具有相同属性及相同行为的一组对象称为类。广义地讲,具有共同性质的事物的集合就称为类。比如我们现实世界中汽车就属于一个类。那么具体的某一辆汽车属于汽车对象。

    类的声明与创建

    类声明

    语法:

    class 类名称
    {
    //类的成员变量
    //类的方法
    }
    

    实例:

    // 声明车类
    class Car{
        // 成员变量
        String color;
        
        // 类变量
        public static int count = 0;
        
        // 类方法
        public void drive(){
            // 局部变量
            int speed = 10;
            System.out.println("驾驶");
        }
        
    }
    

    一个类可以包含以下类型变量:

    • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
    • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
    • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。
    构造方法

    每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。

    在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。

    实例:

    // 声明车类
    class Car{
        // 成员变量
        String color;
        
        //默认构造方法
        public Car(){}
        
        public Car(String color){
        	this.color = color;
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
        }
        
    }
    
    创建对象

    对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:

    • 声明:声明一个对象,包括对象名称和对象类型。
    • 实例化:使用关键字 new 来创建一个对象。
    • 初始化:使用 new 创建对象时,会调用构造方法初始化对象。

    实例:

    // 声明车类
    public class Car{
        // 成员变量
        String color;
        
        //默认构造方法
        public Car(){
            System.out.println("默认构造方法被调用");
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
        }
        
        public static void main(String[] args){
            // 创建对象并使用 , 实例化的时候会调用对应的构造方法
            Car car = new Car();
            car.color = "红色";
            car.drive();
        }
    }
    
    对象使用

    如果要访问对象里的某个成员变量或方法,可以通过下面的语法来实现。

    • 访问属性:对象名称.属性名
    • 访问方法:对象名称.方法名()
    对象比较

    有两种方式可用于对象间的比较,即“= =”运算符与equals()方法。“= =”运算符用于比较两个对象的内存地址值是否相等,equals()方法用于比较两个对象的内容是否一致。

    // 声明车类
    public class Car{
        // 成员变量
        String color;
        
        //默认构造方法
        public Car(){
            System.out.println("默认构造方法被调用");
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
        }
        
        public static void main(String[] args){
            Car car1 = new Car();
            Car car2 = new Car();
            Car car3 = car1;
            
            System.out.println(car1 == car3);
            System.out.println(car1.equals(car2));
        }
    }
    
    对象数组

    在前面已介绍过如何以数组来保存基本数据类型的变量。相同的,对象也可以用数组来存放,可通过下面两个步骤来实现。
    (1) 声明类类型的数组变量,并用new分配内存空间给数组。
    (2) 用new产生新的对象,并分配内存空间给它。

    // 建立Car的数组
    Car[] car = new Car[3]; // 声明Car数组并分配内存空间
    
    // 将对象指向数组对应位置
    car[0] = new Car();
    car[1] = new Car();
    car[2] = new Car();
    

    方法

    方法定义

    所有的方法均在类中声明。定义方法的一般形式如下。

    修饰符 返回类型 方法名(参数列表) {
    	//方法主体
        return 方法返回值;
    }
    

    方法返回值通过return语句实现,如果没有返回值,返回类型设置为void,可以省略return

    private方法

    public方法,自然就有private方法。和private字段一样,private方法不允许外部调用,那我们定义private方法有什么用?

    定义private方法的理由是内部方法是可以调用private方法的。例如:

    // 声明车类
    public class Car{
        // 成员变量
        String color;
        
        //默认构造方法
        public Car(){
            System.out.println("默认构造方法被调用");
        }
        
        /**
         * 车辆发电不需要外部操作,自己内部就可以进行
         */
        private void generateElectricity(){
            System.out.println("自动发电");
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
            // 车辆启动行驶过程中就会发电
            generateElectricity();
        }
    
    }
    
    this变量

    在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。

    如果没有命名冲突,可以省略this。例如:

    // 声明车类
    public class Car{
        // 成员变量
        String color;
        
        //默认构造方法
        public Car(String color){
            this.color = color;
        }
        
        private void generateElectricity(){
            System.out.println("自动发电");
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
            // 车辆启动行驶过程中就会发电
            this.generateElectricity();
        }
    }
    
    方法参数

    方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。例如:

    // 声明车类
    public class Car{
        
        /**
         * 车辆发电不需要外部操作,自己内部就可以进行
         * @param gasoline 汽油
         */
        private void generateElectricity(String gasoline){
            System.out.println("自动发电");
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
            // 车辆启动行驶过程中就会发电
            this.generateElectricity("汽油");
        }
    }
    

    generateElectricity带有一个参数gasoline,调用的时候也需要传递一个参数,如果发电还需要汽油的数量,那么可以这么写:

    // 声明车类
    public class Car{
        
        /**
         * 车辆发电不需要外部操作,自己内部就可以进行
         * @param gasoline 汽油
         * @param count 油量
         */
        private void generateElectricity(String gasoline,int count){
            System.out.println("自动发电");
        }
        
        // 类方法
        public void drive(){
            System.out.println("驾驶");
            // 车辆启动行驶过程中就会发电
            this.generateElectricity("汽油",10);
        }
    }
    
    可变参数

    可变参数用类型...定义,可变参数相当于数组类型:

    public class Car{
        String[] addrArray;
        
        // 汽车导航,需要多个地址
        private void navigation(String... addr){
            System.out.println("导航");
            this.addrArray = addr;
        }
        
    }
    

    深入探讨可以加笔者QQ:1120855315
    点击获取免费Java免费视频
    添加QQ群837949026可以领取更多学习资料

    展开全文
  • 面向对象基础概念

    千次阅读 2007-05-30 17:27:00
    面向对象概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。 谈到面向对象,这方面的文章非常多。...

          OO(Object Oriented,面向对象)是当前计算机界关心的重点,它是90年代软件开发方法的主流。面向对象的概念和应用已超越了程序设计软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。

     

          谈到面向对象,这方面的文章非常多。但是,明确地给出对象的定义或说明对象的定义的非常少——至少我现在还没有发现。其初,“面向对象”是专指在程序设计中采用封装、继承、抽象等设计方法。可是,这个定义显然不能再适合现在情况。面向对象的思想已经涉及到软件开发的各个方面。如,面向对象的分析(OOAObject Oriented Analysis),面向对象的设计(OOD,Object Oriented Design)、以及我们经常说的面向对象的编程实现(OOPObject Oriented Programming)。许多有关面向对象的文章都只是讲述在面向对象的开发中所需要注意的问题或所采用的比较好的设计方法。看这些文章只有真正懂得什么是对象,什么是面向对象,才能最大程度地对自己有所裨益。这一点,恐怕对初学者甚至是从事相关工作多年的人员也会对它们的概念模糊不清。

     

          面向对象是当前计算机界关心的重点,它是90年代软件开发方法的主流。面向对象的概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。 

    一、传统开发方法存在问题

          1.软件重用性差
          重用性是指同一事物不经修改或稍加修改就可多次重复使用的性质。软件重用性是软件工程追求的目标之一。

          2.软件可维护性差
          软件工程强调软件的可维护性,强调文档资料的重要性,规定最终的软件产品应该由完整、一致的配置成分组成。在软件开发过程中,始终强调软件的可读性、可修改性和可测试性是软件的重要的质量指标。实践证明,用传统方法开发出来的软件,维护时其费用和成本仍然很高,其原因是可修改性差,维护困难,导致可维护性差。

          3.开发出的软件不能满足用户需要
          用传统的结构化方法开发大型软件系统涉及各种不同领域的知识,在开发需求模糊或需求动态变化的系统时,所开发出的软件系统往往不能真正满足用户的需要。

          用结构化方法开发的软件,其稳定性、可修改性和可重用性都比较差,这是因为结构化方法的本质是功能分解,从代表目标系统整体功能的单个处理着手,自顶向下不断把复杂的处理分解为子处理,这样一层一层的分解下去,直到仅剩下若干个容易实现的子处理功能为止,然后用相应的工具来描述各个最低层的处理。因此,结构化方法是围绕实现处理功能的“过程”来构造系统的。然而,用户需求的变化大部分是针对功能的,因此,这种变化对于基于过程的设计来说是灾难性的。用这种方法设计出来的系统结构常常是不稳定的 ,用户需求的变化往往造成系统结构的较大变化,从而需要花费很大代价才能实现这种变化。

    二、面向对象的基本概念

          (1)对象。
          对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。

          (2)对象的状态和行为。
          对象具有状态,一个对象用数据值来描述它的状态。
          对象还有操作,用于改变对象的状态,对象及其操作就是对象的行为。
          对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中

          (3)
          具有相同或相似性质的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象。
          类具有属性,它是对象的状态的抽象,用数据结构来描述类的属性。
          类具有操作,它是对象的行为的抽象,用操作名和实现该操作的方法来描述。

          (4)类的结构。
          在客观世界中有若干类,这些类之间有一定的结构关系。通常有两种主要的结构关系,即一般--具体结构关系,整体--部分结构关系。

          ①一般——具体结构称为分类结构,也可以说是“或”关系,或者是“is a”关系。
          ②整体——部分结构称为组装结构,它们之间的关系是一种“与”关系,或者是“has a”关系。

          (5)消息和方法。
          对象之间进行通信的结构叫做消息。在对象的操作中,当一个消息发送给某个对象时,消息包含接收对象去执行某种操作的信息。发送一条消息至少要包括说明接受消息的对象名、发送给该对象的消息名(即对象名、方法名)。一般还要对参数加以说明,参数可以是认识该消息的对象所知道的变量名,或者是所有对象都知道的全局变量名。

          类中操作的实现过程叫做方法,一个方法有方法名、参数、方法体。消息传递如图10-1所示。


    二、面向对象的特征

          (1)对象唯一性。
          每个对象都有自身唯一的标识,通过这种标识,可找到相应的对象。在对象的整个生命期中,它的标识都不改变,不同的对象不能有相同的标识。

          (2)分类性。
          分类性是指将具有一致的数据结构(属性)和行为(操作)的对象抽象成类。一个类就是这样一种抽象,它反映了与应用有关的重要性质,而忽略其他一些无关内容。任何类的划分都是主观的,但必须与具体的应用有关。

          (3)继承性。
          继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。

          继承性是面向对象程序设计语言不同于其它语言的最重要的特点,是其他语言所没有的。

          在类层次中,子类只继承一个父类的数据结构和方法,则称为单重继承。
          在类层次中,子类继承了多个父类的数据结构和方法,则称为多重继承。
          在软件开发中,类的继承性使所建立的软件具有开放性、可扩充性,这是信息组织与分类的行之有效的方法,它简化了对象、类的创建工作量,增加了代码的可重性。
          采用继承性,提供了类的规范的等级结构。通过类的继承关系,使公共的特性能够共享,提高了软件的重用性。

          (4)多态性(多形性)
          多态性使指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。
          多态性允许每个对象以适合自身的方式去响应共同的消息。
          多态性增强了软件的灵活性和重用性。

    三、面向对象的要素

          (1)抽象。
          抽象是指强调实体的本质、内在的属性。在系统开发中,抽象指的是在决定如何实现对象之前的对象的意义和行为。使用抽象可以尽可能避免过早考虑一些细节。

          类实现了对象的数据(即状态)和行为的抽象。
     
          (2)封装性(信息隐藏)。
          封装性是保证软件部件具有优良的模块性的基础。
          面向对象的类是封装良好的模块,类定义将其说明(用户可见的外部接口)与实现(用户不可见的内部实现)显式地分开,其内部实现按其具体定义的作用域提供保护。
          对象是封装的最基本单位。封装防止了程序相互依赖性而带来的变动影响。面向对象的封装比传统语言的封装更为清晰、更为有力。

          (3)共享性
          面向对象技术在不同级别上促进了共享
          同一类中的共享。同一类中的对象有着相同数据结构。这些对象之间是结构、行为特征的共享关系。
          在同一应用中共享。在同一应用的类层次结构中,存在继承关系的各相似子类中,存在数据结构和行为的继承,使各相似子类共享共同的结构和行为。使用继承来实现代码的共享,这也是面向对象的主要优点之一。
          在不同应用中共享。面向对象不仅允许在同一应用中共享信息,而且为未来目标的可重用设计准备了条件。通过类库这种机制和结构来实现不同应用中的信息共享。

          4.强调对象结构而不是程序结构

    四、面向对象的开发方法

          目前,面向对象开发方法的研究已日趋成熟,国际上已有不少面向对象产品出现。面向对象开发方法有Coad方法、Booch方法和OMT方法等。

          1.Booch方法

          Booch最先描述了面向对象的软件开发方法的基础问题,指出面向对象开发是一种根本不同于传统的功能分解的设计方法。面向对象的软件分解更接近人对客观事务的理解,而功能分解只通过问题空间的转换来获得。

          2.Coad方法

          Coad方法是1989年Coad和Yourdon提出的面向对象开发方法。该方法的主要优点是通过多年来大系统开发的经验与面向对象概念的有机结合,在对象、结构、属性和操作的认定方面,提出了一套系统的原则。该方法完成了从需求角度进一步进行类和类层次结构的认定。尽管Coad方法没有引入类和类层次结构的术语,但事实上已经在分类结构、属性、操作、消息关联等概念中体现了类和类层次结构的特征。

          3.OMT方法

          OMT方法是1991年由James Rumbaugh等5人提出来的,其经典著作为“面向对象的建模与设计”。

          该方法是一种新兴的面向对象的开发方法,开发工作的基础是对真实世界的对象建模,然后围绕这些对象使用分析模型来进行独立于语言的设计,面向对象的建模和设计促进了对需求的理解,有利于开发得更清晰、更容易维护的软件系统。该方法为大多数应用领域的软件开发提供了一种实际的、高效的保证,努力寻求一种问题求解的实际方法。

          4.UML(Unified Modeling Language)语言

          软件工程领域在1995年~1997年取得了前所未有的进展,其成果超过软件工程领域过去15年的成就总和,其中最重要的成果之一就是统一建模语言(UML)的出现。UML将是面向对象技术领域内占主导地位的标准建模语言
    UML不仅统一了Booch方法、OMT方法、OOSE方法的表示方法,而且对其作了进一步的发展,最终统一为大众接受的标准建模语言。UML是一种定义良好、易于表达、功能强大且普遍适用的建模语言。它融入了软件工程领域的新思想、新方法和新技术。它的作用域不限于支持面向对象的分析与设计,还支持从需求分析开始的软件开发全过程。

    五、面向对象的模型

     ·对象模型

          对象模型表示了静态的、结构化的系统数据性质,描述了系统的静态结构,它是从客观世界实体的对象关系角度来描述,表现了对象的相互关系。该模型主要关心系统中对象的结构、属性和操作,它是分析阶段三个模型的核心,是其他两个模型的框架

          1.对象和类

          (1) 对象。
          对象建模的目的就是描述对象。

          (2) 类。
          通过将对象抽象成类,我们可以使问题抽象化,抽象增强了模型的归纳能力。

          (3) 属性。
          属性指的是类中对象所具有的性质(数据值)。

          (4) 操作和方法。

          操作是类中对象所使用的一种功能或变换。类中的各对象可以共享操作,每个操作都有一个目标对象作为其隐含参数。
      方法是类的操作的实现步骤。

          2.关联和链
          关联是建立类之间关系的一种手段,而链则是建立对象之间关系的一种手段。

          (1) 关联和链的含义。
          链表示对象间的物理与概念联结,关联表示类之间的一种关系,链是关联的实例,关联是链的抽象。

          (2) 角色。
          角色说明类在关联中的作用,它位于关联的端点。

          (3) 受限关联。
          受限关联由两个类及一个限定词组成,限定词是一种特定的属性,用来有效的减少关联的重数,限定词在关联的终端对象集中说明。
          限定提高了语义的精确性,增强了查询能力,在现实世界中,常常出现限定词。

          (4) 关联的多重性。
          关联的多重性是指类中有多少个对象与关联的类的一个对象相关。重数常描述为“一”或“多”。
          图10-8表示了各种关联的重数。小实心圆表示“多个”,从零到多。小空心圆表示零或一。没有符号表示的是一对一关联。

          3.类的层次结构
          (1) 聚集关系。
          聚集是一种“整体-部分”关系。在这种关系中,有整体类和部分类之分。聚集最重要的性质是传递性,也具有逆对称性。


          聚集可以有不同层次,可以把不同分类聚集起来得到一颗简单的聚集树,聚集树是一种简单表示,比画很多线来将部分类联系起来简单得多,对象模型应该容易地反映各级层次,图10-10表示一个关于微机的多极聚集。

           (2)一般化关系。
          一般化关系是在保留对象差异的同时共享对象相似性的一种高度抽象方式。它是“一般---具体”的关系。一般化类称为你类,具体类又能称为子类,各子类继承了交类的性质,而各子类的一些共同性质和操作又归纳到你类中。因此,一般化关系和继承是同时存在的。一般化关系的符号表示是在类关联的连线上加一个小三角形,如图10-11

          4.对象模型
          (1)模板。模板是类、关联、一般化结构的逻辑组成。
          (2)对象模型。

          对象模型是由一个或若干个模板组成。模板将模型分为若干个便于管理的子块,在整个对象模型和类及关联的构造块之间,模板提供了一种集成的中间单元,模板中的类名及关联名是唯一的。

    ·动态模型

          动态模型是与时间和变化有关的系统性质。该模型描述了系统的控制结构,它表示了瞬间的、行为化的系统控制
    性质,它关心的是系统的控制,操作的执行顺序,它表示从对象的事件和状态的角度出发,表现了对象的相互行为。
    该模型描述的系统属性是触发事件、事件序列、状态、事件与状态的组织。使用状态图作为描述工具。它涉及到事件、状态、操作等重要概念。
           1.事件
          事件是指定时刻发生的某件事。

           2.状态
           状态是对象属性值的抽象。对象的属性值按照影响对象显著行为的性质将其归并到一个状态中去。状态指明了对象
    对输入事件的响应。

          3.状态图

          状态图是一个标准的计算机概念,他是有限自动机的图形表示,这里把状态图作为建立动态模型的图形工具。
          状态图反映了状态与事件的关系。当接收一事件时,下一状态就取决于当前状态和所接收的该事件,由该事件引起的状态变化称为转换。
          状态图是一种图,用结点表示状态,结点用圆圈表示;圆圈内有状态名,用箭头连线表示状态的转换,上面标记事件名,箭头方向表示转换的方向。

    ·功能模型

          功能模型描述了系统的所有计算。功能模型指出发生了什么,动态模型确定什么时候发生,而对象模型确定发生的客体。功能模型表明一个计算如何从输入值得到输出值,它不考虑计算的次序。功能模型由多张数据流图组成。数据流图用来表示从源对象到目标对象的数据值的流向,它不包含控制信息,控制信息在动态模型中表示,同时数据流图也不表示对象中值的组织,值的组织在对象模型中表示。图10-15给出了一个窗口系统的图标显示的数据流图。

           数据流图中包含有处理、数据流、动作对象和数据存储对象。

          1.处理
           数据流图中的处理用来改变数据值。最低层处理是纯粹的函数,一张完整的数据流图是一个高层处理。

           2.数据流
           数据流图中的数据流将对象的输出与处理、处理与对象的输入、处理与处理联系起来。在一个计算机中,用数据流来表示一中间数据值,数据流不能改变数据值。

          3.动作对象
           动作对象是一种主动对象,它通过生成或者使用数据值来驱动数据流图。

          4.数据存储对象
          数据流图中的数据存储是被动对象,它用来存储数据。它与动作对象不一样,数据存储本身不产生任何操作,它只响应存储和访问的要求。


    六、面向对象的分析

          面向对象分析的目的是对客观世界的系统进行建模。本节以上面介绍的模型概念为基础,结合“银行网络系统”的具体实例来构造客观世界问题的准确、严密的分析模型。
          分析模型有三种用途:用来明确问题需求;为用户和开发人员提供明确需求;为用户和开发人员提供一个协商的基础,作为后继的设计和实现的框架。

    (一) 面向对象的分析

         系统分析的第一步是:陈述需求。分析者必须同用户一块工作来提炼需求,因为这样才表示了用户的真实意图,其中涉及对需求的分析及查找丢失的信息。下面以“银行网络系统”为例,用面向对象方法进行开发。
    银行网络系统问题陈述: 设计支持银行网络的软件,银行网络包括人工出纳站和分行共享的自动出纳机。每个分理处用分理处计算机来保存各自的帐户,处理各自的事务;各自分理处的出纳站与分理处计算机通信,出纳站录入帐户和事务数据;自动出纳机与分行计算机通信,分行计算机与拨款分理处结帐,自动出纳机与用户接口接受现金卡,与分行计算机通信完成事务,发放现金,打印收据;系统需要记录保管和安全措施;系统必须正确处理同一帐户的并发访问;每个分处理为自己的计算机准备软件,银行网络费用根据顾客和现金卡的数目分摊给各分理处。
    图10-18给出银行网络系统的示意图。


    (二)建立对象模型

          首先标识和关联,因为它们影响了整体结构和解决问题的方法,其次是增加属性,进一步描述类和关联的基本网络,使用继承合并和组织类,最后操作增加到类中去作为构造动态模型和功能模型的副产品。

          1.确定类

          构造对象模型的第一步是标出来自问题域的相关的对象类,对象包括物理实体和概念。所有类在应用中都必须有意义,在问题陈述中,并非所有类都是明显给出的。有些是隐含在问题域或一般知识中的。

    按图10-19所示的过程确定类
       

    查找问题陈述中的所有名词,产生如下的暂定类。
     软件            银行网络         出纳员        自动出纳机           分行
     分处理          分处理计算机     帐户          事务                 出纳站
     事务数据        分行计算机       现金卡        用户                 现金
     收据            系统             顾客          费用                 帐户数据
     访问            安全措施         记录保管

         根据下列标准,去掉不必要的类和不正确的类。
     (1) 冗余类:若两个类表述了同一个信息 ,保留最富有描述能力的类。如"用户"和"顾客"就是重复的描述,因为"顾客"最富有描述性,因此保留它。
     (2) 不相干的类:除掉与问题没有关系或根本无关的类。例如,摊派费用超出了银行网络的范围。
     (3) 模糊类:类必须是确定的,有些暂定类边界定义模糊或范围太广,如"记录保管"就模糊类,它是"事务"中的一部分。
     (4) 属性:某些名词描述的是其他对象的属性,则从暂定类中删除。如果某一性质的独立性很重要,就应该把他归属到类,而不把它作为属性。
     (5) 操作:如果问题陈述中的名词有动作含义,则描述的操作就不是类。但是具有自身性质而且需要独立存在的操作应该描述成类。如我们只构造电话模型,"拨号"就是动态模型的一部分而不是类,但在电话拨号系统中,"拨号"是一个重要的类,它日期、时间、受话地点等属性。

          在银行网络系统中,模糊类是"系统"、"安全措施"、"记录保管"、"银行网络"等。属于属性的有:"帐户数据"、"收据"、"现金"、"事务数据"。属于实现的如:"访问"、"软件"等。这些均应除去。

       2.准备数据字典

       为所有建模实体准备一个数据字典。准确描述各个类的精确含义,描述当前问题中的类的范围,包括对类的成员、用法方面的假设或限制。

       3.确定关联

       两个或多个类之间的相互依赖就是关联。一种依赖表示一种关联,可用各种方式来实现关联,但在分析模型中应删除实现的考虑,以便设计时更为灵活。关联常用描述性动词或动词词组来表示,其中有物理位置的表示、传导的动作、通信、所有者关系、条件的满足等。从问题陈述中抽取所有可能的关联表述,把它们记下来,但不要过早去细化这些表述。

      下面是银行网络系统中所有可能的关联,大多数是直接抽取问题中的动词词组而得到的。在陈述中,有些动词词组表述的关联是不明显的。最后,还有一些关联与客观世界或人的假设有关,必须同用户一起核实这种关联,因为这种关联在问题陈述中找不到。

       银行网络问题陈述中的关联:
     ·银行网络包括出纳站和自动出纳机;
     ·分行共享自动出纳机;
     ·分理处提供分理处计算机;
     ·分理处计算机保存帐户;
     ·分理处计算机处理帐户支付事务;
     ·分理处拥有出纳站;
     ·出纳站与分理处计算机通信;
     ·出纳员为帐户录入事务;
     ·自动出纳机接受现金卡;
     ·自动出纳机与用户接口;
     ·自动出纳机发放现金;
     ·自动出纳机打印收据;
     ·系统处理并发访问;
     ·分理处提供软件;
     ·费用分摊给分理处。
      隐含的动词词组:
     ·分行由分理处组成;
     ·分理处拥有帐户;
     ·分行拥有分行计算机;
     ·系统提供记录保管;
     ·系统提供安全;
     ·顾客有现金卡。
      基于问题域知识的关联:
     ·分理处雇佣出纳员;
     ·现金卡访问帐户。

      使用下列标准去掉不必要和不正确的关联:

     (1) 若某个类已被删除,那么与它有关的关联也必须删除或者用其它类来重新表述。在例中,我们删除了"银行网络",相关的关联也要删除。
     (2) 不相干的关联或实现阶段的关联:删除所有问题域之外的关联或涉及实现结构中的关联。如"系统处理并发访问"就是一种实现的概念。
     (3) 动作:关联应该描述应用域的结构性质而不是瞬时事件,因此应删除"自动出纳机接受现金卡","自动出纳机与用户接口"等。
     (4) 派生关联:省略那些可以用其他关联来定义的关联。因为这种关联是冗余的。银行网络系统的初步对象图如图10-20所示。其中含有关联。

         4.确定属性

       属性是个体对象的性质,属性通常用修饰性的名词词组来表示.形容词常常表示具体的可枚举的属性值,属性不可能在问题陈述中完全表述出来,必须借助于应用域的知识及对客观世界的知识才可以找到它们。只考虑与具体应用直接相关的属性,不要考虑那些超出问题范围的属性。首先找出重要属性,避免那些只用于实现的属性,要为各个属性取有意义的名字。按下列标准删除不必要的和不正确的属性:

     (1) 对象:若实体的独立存在比它的值重要,那么这个实体不是属性而是对象。如在邮政目录中,"城市"是一个属性,然而在人口普查中,"城市"则被看作是对象。在具体应用中,具有自身性质的实体一定是对象。
     (2) 定词:若属性值取决于某种具体上下文,则可考虑把该属性重新表述为一个限定词。
     (3) 名称:名称常常作为限定词而不是对象的属性,当名称不依赖于上下文关系时,名称即为一个对象属性,尤其是它不惟一时。
     (4) 标识符:在考虑对象模糊性时,引入对象标识符表示,在对象模型中不列出这些对象标识符,它是隐含在对象模型中,只列出存在于应用域的属性。
     (5) 内部值:若属性描述了对外不透明的对象的内部状态,则应从对象模型中删除该属性。
     (6) 细化:忽略那些不可能对大多数操作有影响的属性。

      5.使用继承来细化类

       使用继承来共享公共机构,以次来组织类,可以用两种方式来进行。
     (1) 自底向上通过把现有类的共同性质一般化为父类,寻找具有相似的属性,关系或操作的类来发现继承。例如"远程事务"和"出纳事务"是类似的,可以一般化为"事务"。有些一般化结构常常是基于客观世界边界的现有分类,只要可能,尽量使用现有概念。对称性常有助于发现某些丢失的类。
     (2) 自顶向下将现有的类细化为更具体的子类。具体化常常可以从应用域中明显看出来。应用域中各枚举字情况是最常见的具体化的来源。例如:菜单,可以有固定菜单,顶部菜单,弹出菜单,下拉菜单等,这就可以把菜单类具体细化为各种具体菜单的子类。当同一关联名出现多次且意义也相同时,应尽量具体化为相关联的类,例如"事务"从"出纳站"和"自动出纳机"进入,则"录入站"就是"出纳站"和"自动出纳站"的一般化。在类层次中,可以为具体的类分配属性和关联。各属性和都应分配给最一般的适合的类,有时也加上一些修正。

          应用域中各枚举情况是最常见的具体化的来源。

       6.完善对象模型

       对象建模不可能一次就能保证模型是完全正确的,软件开发的整个过程就是一个不断完善的过程。模型的不同组成部分多半是在不同的阶段完成的,如果发现模型的缺陷,就必须返回到前期阶段去修改,有些细化工作是在动态模型和功能模型完成之后才开始进行的。
      (1) 几种可能丢失对象的情况及解决办法:
     ·同一类中存在毫无关系的属性和操作,则分解这个类,使各部分相互关联;
     ·一般化体系不清楚,则可能分离扮演两种角色的类
     ·存在无目标类的操作,则找出并加上失去目标的类;
     ·存在名称及目的相同的冗余关联,则通过一般化创建丢失的父类,把关联组织在一起。

      (2) 查找多余的类。
      类中缺少属性,操作和关联,则可删除这个类。

      (3)查找丢失的关联。
       丢失了操作的访问路径,则加入新的关联以回答查询。

      (4) 网络系统的具体情况作如下的修改:
     ①现金卡有多个独立的特性。把它分解为两个对象:卡片权限和现金卡。
     a.卡片权限:它是银行用来鉴别用户访问权限的卡片,表示一个或多个用户帐户的访问权限;各个卡片权限对象中可能具有好几个现金卡,每张都带有安全码,卡片码,它们附在现金卡上,表现银行的卡片权限。
     b.现金卡:它是自动出纳机得到表示码的数据卡片,它也是银行代码和现金卡代码的数据载体。
     ②"事务"不能体现对帐户之间的传输描述的一般性,因它只涉及一个帐户,一般来说,在每个帐户中,一个"事务"包括一个或多个"更新",一个"更新"是对帐户的一个动作,它们是取款,存款,查询之一。一个"更新"中所有"更新"应该是一个原子操作。
     ③"分理处"和"分离处理机"之间,"分行"和"分行处理机"之间的区别似乎并不影响分析,计算机的通信处理实际上是实现的概念,将"分理处计算机"并入到"分理处",将"分行计算机"并入到"分行"。

    (三)建立动态模型

       1.准备脚本
       动态分析从寻找事件开始,然后确定各对象的可能事件顺序。在分析阶段不考虑算法的执行,算法是实现模型的一部分。

       2.确定事件
       确定所有外部事件。事件包括所有来自或发往用户的信息、外部设备的信号、输入、转换和动作,可以发现正常事件,但不能遗漏条件和异常事件。

       3.准备事件跟踪表
       把脚本表示成一个事件跟踪表,即不同对象之间的事件排序表,对象为表中的列,给每个对象分配一个独立的列。


       4.构造状态图
       对各对象类建立状态图,反映对象接收和发送的事件,每个事件跟踪都对应于状态图中一条路径。

    (四)建立功能建模

       功能模型用来说明值是如何计算的,表明值之间的依赖关系及相关的功能,数据流图有助于表示功能依赖关系,其中的处理应于状态图的活动和动作,其中的数据流对应于对象图中的对象或属性。

       1.确定输入值、输出值
       先列出输入、输出值,输入、输出值是系统与外界之间的事件的参数。

       2.建立数据流图
       数据流图说明输出值是怎样从输入值得来的,数据流图通常按层次组织。

    (五)确定操作

       在建立对象模型时,确定了类、关联、结构和属性,还没有确定操作。只有建立了动态模型和功能模型之后,才可能最后确定类的操作。

    七、面向对象的设计

       面向对象设计是把分析阶段得到的需求转变成符合成本和质量要求的、抽象的系统实现方案的过程。从面向对象分析到面向对象设计,是一个逐渐扩充模型的过程。

       瀑布模型把设计进一步划分成概要设计和详细设计两个阶段,类似地,也可以把面向对象设计再细分为系统设计和对象设计。系统设计确定实现系统的策略和目标系统的高层结构。对象设计确定解空间中的类、关联、接口形式及实现操作的算法。

    (一)面向对象设计的准则

       1.模块化
       面向对象开发方法很自然地支持了把系统分解成模块的设计原则:对象就是模块。它是把数据结构和操作这些数据的方法紧密地结合在一起所构成的模块。
       2.抽象
       面向对象方法不仅支持过程抽象,而且支持数据抽象。
       3.信息隐藏
       在面向对象方法中,信息隐藏通过对象的封装性来实现。
       4.低耦合
       在面向对象方法中,对象是最基本的模块,因此,耦合主要指不同对象之间相互关联的紧密程度。低耦合是设计的一个重要标准,因为这有助于使得系统中某一部分的变化对其它部分的影响降到最低程度。
       5.高内聚
       (1)操作内聚。
       (2)类内聚。
       (3)一般——具体内聚。

    (二)面向对象设计的启发规则

       1.设计结果应该清晰易懂
       使设计结果清晰、易懂、易读是提高软件可维护性和可重用性的重要措施。显然,人们不会重用那些他们不理解的设计。
       要做到:
       (1)用词一致。
       (2)使用已有的协议。
       (3)减少消息模式的数量。
       (4)避免模糊的定义。

       2.一般——具体结构的深度应适当

       3.设计简单类
       应该尽量设计小而简单的类,这样便以开发和管理。为了保持简单,应注意以下几点:
       (1)避免包含过多的属性。
       (2)有明确的定义。
       (3)尽量简化对象之间的合作关系。
       (4)不要提供太多的操作。

       4.使用简单的协议
       一般来说,消息中参数不要超过3个。

       5.使用简单的操作
       面向对象设计出来的类中的操作通常都很小,一般只有3至5行源程序语句,可以用仅含一个动词和一个宾语的简单句子描述它的功能

       6.把设计变动减至最小
       通常,设计的质量越高,设计结果保持不变的时间也越长。即使出现必须修改设计的情况,也应该使修改的范围尽可能小。
    (三)系统设计

       系统设计是问题求解及建立解答的高级策略。必须制定解决问题的基本方法,系统的高层结构形式包括子系统的分解、它的固有并发性、子系统分配给硬软件、数据存储管理、资源协调、软件控制实现、人机交互接口。

    1.系统设计概述

        设计阶段先从高层入手,然后细化。系统设计要决定整个结构及风格,这种结构为后面设计阶段的更详细策略的设计提供了基础。

       (1)系统分解。
       系统中主要的组成部分称为子系统,子系统既不是一个对象也不是一个功能,而是类、关联、操作、事件和约束的集合。
       (2)确定并发性。
       分析模型、现实世界及硬件中不少对象均是并发的。
       (3)处理器及任务分配。
       各并发子系统必须分配给单个硬件单元,要么是一个一般的处理器,要么是一个具体的功能单元。
       (4)数据存储管理。
       系统中的内部数据和外部数据的存储管理是一项重要的任务。通常各数据存储可以将数据结构、文件、数据库组合在一起,不同数据存储要在费用、访问时间、容量及可靠性之间做出折衷考虑。
       (5)全局资源的处理。
       必须确定全局资源,并且制定访问全局资源的策略。
       (6)选择软件控制机制。
       分析模型中所有交互行为都表示为对象之间的事件。系统设计必须从多种方法中选择某种方法来实现软件的控制。
       (7)人机交互接口设计。
       设计中的大部分工作都与稳定的状态行为有关,但必须考虑用户使用系统的交互接口。

       2.系统结构的一般框架

       3.系统分解——建立系统的体系结构
       可用的软件库以及程序员的编程经验。
       通过面向对象分析得到的问题域精确模型,为设计体系结构奠定了良好的基础,建立了完整的框架。

       4.选择软件控制机制
       软件系统中存在两种控制流,外部控制流和内部控制流。

       5.数据存储管理
       数据存储管理是系统存储或检索对象的基本设施,它建立在某种数据存储管理系统之上,并且隔离了数据存储管理模式的影响。

       6.设计人机交互接口
       在面向对象分析过程中,已经对用户界面需求作了初步分析,在面向对象设计过程中,则应该对系统的人机交互接口进行详细设计,以确定人机交互的细节,其中包括指定窗口和报表的形式、设计命令层次等项内容。

    (四)对象设计
       1.对象设计概述
       2.三种模型的结合
       (1)获得操作。
       (2)确定操作的目标对象。
       3.算法设计
       4.优化设计
       5.控制的实现
       6.调整继承
       7.关联的设计
                                   
    八、面向对象的实现

    (一)程序设计语言

       1.选择面向对象语言
       采用面向对象方法开发软件的基本目的和主要优点是通过重用提高软件的生产率。因此,应该优先选用能够最完善、最准确地表达问题域语义的面向对象语言。

       在选择编程语言时,应该考虑的其他因素还有:对用户学习面向对象分析、设计和编码技术所能提供的培训操作;在使用这个面向对象语言期间能提供的技术支持;能提供给开发人员使用的开发工具、开发平台,对机器性能和内存的需求,集成已有软件的容易程度。

       2.程序设计风格
       (1)提高重用性。
       (2)提高可扩充性。
       (3)提高健壮性。

    (二)类的实现

       在开发过程中,类的实现是核心问题。在用面向对象风格所写的系统中,所有的数据都被封装在类的实例中。而整个程序则被封装在一个更高级的类中。在使用既存部件的面向对象系统中,可以只花费少量时间和工作量来实现软件。只要增加类的实例,开发少量的新类和实现各个对象之间互相通信的操作,就能建立需要的软件。

       一种方案是先开发一个比较小、比较简单的来,作为开发比较大、比较复杂的类的基础。

       (1)“原封不动”重用。
       (2)进化性重用。
       一个能够完全符合要求特性的类可能并不存在。
       (3)“废弃性”开发。
       不用任何重用来开发一个新类。
       (4)错误处理。
       一个类应是自主的,有责任定位和报告错误。

    (三)应用系统的实现

       应用系统的实现是在所有的类都被实现之后的事。实现一个系统是一个比用过程性方法更简单、更简短的过程。有些实例将在其他类的初始化过程中使用。而其余的则必须用某种主过程显式地加以说明,或者当作系统最高层的类的表示的一部分。

       在C++和C中有一个main( )函数,可以使用这个过程来说明构成系统主要对象的那些类的实例。

    (四)面向对象测试

       (1)算法层。
       (2)类层。
       测试封装在同一个类中的所有方法和属性之间的相互作用。
       (3)模板层。
       测试一组协同工作的类之间的相互作用。
       (4)系统层。
       把各个子系统组装成完整的面向对象软件系统,在组装过程中同时进行测试。

    九、面向对象和基于对象的区别

          很多人没有区分“面向对象”和“基于对象”两个不同的概念。面向对象的三大特点(封装,继承,多态)却一不可。通常“基于对象”是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。而“多态”表示为父类类型的子类对象实例,没有了继承的概念也就无从谈论“多态”。现在的很多流行技术都是基于对象的,它们使用一些封装好的对象,调用对象的方法,设置对象的属性。但是它们无法让程序员派生新对象类型。他们只能使用现有对象的方法和属性。所以当你判断一个新的技术是否是面向对象的时候,通常可以使用后两个特性来加以判断。“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”没有实现这些,的确很饶口。

          从事面向对象编程的人按照分工来说,可以分为“类库的创建者”和“类库的使用者”。使用类库的人并不都是具备了面向对象思想的人,通常知道如何继承和派生新对象就可以使用类库了,然而我们的思维并没有真正的转过来,使用类库只是在形式上是面向对象,而实质上只是库函数的一种扩展。

          面向对象是一种思想,是我们考虑事情的方法,通常表现为我们是将问题的解决按照过程方式来解决呢,还是将问题抽象为一个对象来解决它。很多情况下,我们会不知不觉的按照过程方式来解决它,而不是考虑将要解决问题抽象为对象去解决它。有些人打着面向对象的幌子,干着过程编程的勾当。

    展开全文
  • 面向对象测试题

    2019-09-30 11:42:09
    1.下列不属于面向对象编程的特性的是(C )。 A.封装 B.继承 C.抽象 D.多态 2.(*)下述概念不属于面向对象的是(D)。 A.对象 B.继承、多态 C.类、封装 D.过程调用 ...

    1.下列不属于面向对象编程的特性的是(  C )。

    A.封装                         B.继承

    C.抽象                         D.多态

    2.(*)下述概念中不属于面向对象的是( D )。

    A.对象                       B.继承、多态

    C.类、封装                     D.过程调用

    3.(*)对象是Java中很重要的概念,下列说法中能准确地描述对象的是( A   )。

    A.对象是类的具体实例,可以操作数据的方法

    B.对象是抽象的,类可以通过对象来生成

    C.对象只是方法的集合

    D.对象是一组具有共同的结构和行为的类

    4.(*)下面有关变量及其作用域的陈述哪一项是错误的?(D

    A.在方法里面定义的局部变量在方法退出的时候被撤销

    B.局部变量只在定义它的方法内有效

    C.在方法外面定义的实例变量在对象被构造时创建

    D.在方法中定义的方法的参变量只要该对象被需要就一直存在

    5.下列方法的声明中不合法的是(B )。

    A.float play(){ return 1; }

    B.void play(int d,e) { }

    C.double play(int d) { return 2.0; }

    D.int play(int r) { return 1; }

    6.下列哪个方法不能与方法public void add(int a){ }重载?(A

    A.public int add(int b) { }

    B.public void add(double b) { }

    C.public void add(int a, int b) { }

    D.public void add(float g) { }

    7.类Test定义如下:

    1.public class Test {

    2.    float use(float a, float b) {

    3.    }

    4.  

    5.}

    将以下哪种方法插入第4行是不合法的?( B

    A.float use(float a, float b, float c) {  }

    B.float use(float c, float d) {  }

    C.int use(int a,int b) {  }

    D.float use(int a, int b, int c) {  }

     

     

    8.为了区分重载多态中同名的不同方法,要求(   A )。

    A.采用不同的参数列表

    B.返回值类型不同

    C.调用时用类名或对象名做前缀

    D.参数名不同

    9.下列有关构造方法描述正确的是(    D)。

    A.所有类都必须定义一个构造方法

    B.构造方法必须有返回值

    C.构造方法必须访问类的非静态成员

    D.构造方法可以初始化类的成员变量

    10.下列关于构造方法的叙述中,错误的是(C   )。

    A.构造方法名与类名必须相同

    B.构造方法没有返回值,但不用void声明

    C.构造方法不可以重载

    D.构造方法只能通过new自动调用

    11.设A为已定义的类名,下列声明对象a的语句中正确的是(C)。

    A.public A a =new A( );

    B.A a = A( );

    C.A a = new A();

    D.a A;

    12.给出如下类定义:

    public class Test {

    Test(int i) {

    }

    }

    如果要创建一个该类的对象,正确的语句是( B )。

    A.Test t = newTest();

    B.Test t = newTest(5);

    C.Test t = newTest("5");

    D.Test t = newTest(3.4);

    13.以下代码的调试结果为(C  )。

    class Square {

        int a;

        void Square() {

            a = 10;

        }

        public static void main(String[] args) {

            Square s = new Square();

            System.out.println(s.a);

        }

    }

    A.输出10                       B.编译错误

    C.输出0                        D.运行错误

    14.下面程序的输出结果是()。

    class Test {

        int i = 2;

        String s = null;

        void Test() {

            i = 3;

            s = "days";

        }

        public static void main(String args[]) {

            Test t = new Test();

            System.out.println(t.i + t.s);

        }

    }

    A.2null                        B.3null

    C.3days                        D.以上都不对

    15.编译并运行下面的Java程序,将产生什么结果?(D  )

    class A {

        int i = 1;

        int j;

        public static void main(String[] args) {

            int k;

            A a = new A();

            System.out.println(a.i + a.j + k);

        }

    }

    A.0                            B.1

    C.2                            D.出现编译错误

    16.如下代码的输出结果是(  C )。

    public class Test {

        void printValue(int m) {

            do {

                System.out.println("The value is " + m);

            } while (--m > 10);

        }

        public static void main(String arg[]) {

            int i = 10;

            Test t = new Test();

            t.printValue(i);

        }

    }

    A.The value is8               B.Thevalue is 9

    C.The value is10              D.Thevalue is 11

    17.如下代码的输出结果是(  D )。

    class Point {

        int x, y;

        Point(int a, int b) {

            x = a;

            y = b;

        }

    }

    class Test {

        public static void main(String[] args) {

            Point p1, p2;

            p1 = new Point(3, 6);

            p2 = new Point(8, 9);

            p1 = p2;

    System.out.println("p1.x= " + p1.x + ", p1.y = " + p1.y);

        }

    }

    A.p1.x = 3, p1.y = 6           B.p1.x = 3, p1.y= 9

    C.p1.x = 8, p1.y = 6           D.p1.x = 8, p1.y= 9

    二、简答题

    1.简述面向对象编程的三个主要特征。

    封装:客观事物封装成抽象的类

    继承:子类继承父类的所有特征和行为

    多态:父类引用变量可以指向子类对象

     

    2.什么是方法的重载?

    在同一个类中定义多个同名的方法,

    方法名一样,参数类型或者个数不一样

     

    3.简述成员变量和局部变量的区别?

    成员变量:作为类的成员直接存在类中,可以通过this引用

    局部变量:在方法定义或声明上

    内存位置不同:成员变量在堆,局部变量在栈

    成员变量有默认值初始化

    局部变量没有,必须定义赋值才能

     

    4.在写好java文件后,编译时会编译java文件中的注释代码吗?

    不会   只识别代码

    5.什么是方法的重写?

     

    子类继承父类方法,方法名一样,参数列表一样

    而且只能重写父类非私有的方法

     

    三、编程题

    1.定义立方体类Cube,具有属性边长和颜色,具有方法设置颜色和计算体积,在该类的主方法中创建一个立方体对象,将该对象的边长设置为3,颜色设置为“green”,输出该立方体的体积和颜色。4

    public class Cube {
    
         static double side;
    
         static String color;
    
     
    
         Cube(double side){
    
             this.side=side;
    
         }
    
         public static double bulk(){
    
             return side*side*side;
    
         }
    
         public static void setColor(String c){
    
             color=c;
    
         }
    
    }
    
    public class Test {
     
    
         public static void main(String[] args) {
    
             Cube c=new Cube(4);
    
             c.setColor("green");
    
             System.out.println("立方体的体积为:"+c.bulk());
    
             System.out.println(" 颜色为:"+c.color);
    
            
    
         }
    
     
    
    }

     

     

     

    2.编写Java应用程序,该程序中有梯形类和主类。要求如下:8

    (1)梯形类具有属性上底、下底、高和面积,具有返回面积的功能,在构造方法中对上底、下底和高进行初始化。

    (2)主类用来测试梯形类的功能。

     1 public class Ladder {
     2 
     3      double upper;
     4 
     5      double bottom;
     6 
     7      double height;
     8 
     9      double area;
    10 
    11      public Ladder(double upper, double bottom, double height) {
    12 
    13          super();
    14 
    15          this.upper = upper;
    16 
    17          this.bottom = bottom;
    18 
    19          this.height = height;
    20 
    21      }
    22 
    23      //返回面积
    24 
    25      public double getArea(){
    26 
    27          area=(upper+bottom)*height/2;
    28 
    29          return area;
    30 
    31      }
    32 
    33 }
    34 
    35 测试类:
    36 
    37 public class Zhu {
    38 
    39      public static void main(String[] args) {
    40 
    41          Ladder lad=new Ladder(2, 5, 9);
    42 
    43          System.out.println("梯形的面积为:"+lad.getArea());
    44 
    45      }
    46 
    47 }

     

     

     

     

    3.按要求编写Java应用程序:8

    (1)定义描述学生的类Student,有一个构造方法对属性进行初始化,一个outPut方法用于输出学生的信息。

    (2)定义主类,创建两个Student类的对象,测试其功能。

     1 public class Student {
     2 
     3      String name;
     4 
     5      String sex;
     6 
     7      int age;
     8 
     9      public Student(String name, String sex, int age) {
    10 
    11          super();
    12 
    13          this.name = name;
    14 
    15          this.sex = sex;
    16 
    17          this.age = age;
    18 
    19      }
    20 
    21      public void outPut(){
    22 
    23          System.out.println("学生的姓名为:"+this.name+"\t性别为:"+this.sex+"\t年龄为:"+this.age);
    24 
    25      }
    26 
    27 }
    28 
    29 public class TestStu {
    30 
    31      public static void main(String[] args) {
    32 
    33          Student stu=new Student("夏目", "男", 20);
    34 
    35          stu.outPut();
    36 
    37          Student stu1=new Student("娘口三三", "男", 18);
    38 
    39          stu1.outPut();
    40 
    41      }
    42 
    43 }

     

     

     

     

     

    4.(*)定义机动车类,具有属性:车牌号、车速和载重量,具有功能:加速、减速。一个构造方法没有形参,在方法中将车牌号设置为“辽A1234”,速度为100,载重量为50;另一个构造方法用形参为对象的所有属性赋值。5

    在主类中创建两个机动车对象,创建第一个时调用无参数的构造方法,使其加速10。创建第二个时调用有参数的构造方法,使其车牌号为“辽B5678”,车速为80,载重量为20,并让其减速20。最后输出两辆车的所有信息。

     1 public class Car {
     2 
     3      String num;
     4 
     5      double speed;
     6 
     7      double weight;
     8 
     9      double speedUp;
    10 
    11      double speedDown;
    12 
    13      Car(){
    14 
    15          num="辽A1234";
    16 
    17          speed=100;
    18 
    19          weight=50;
    20 
    21      }
    22 
    23      Car(String n, double s, double w) {
    24 
    25          num=n;
    26 
    27          speed=s;
    28 
    29          weight=w;
    30 
    31      }
    32 
    33      public void speedUp(int s) {
    34 
    35          speed+=s;
    36 
    37         
    38 
    39      }
    40 
    41      public void speedDown(int d){
    42 
    43          speed-=d;
    44 
    45      }
    46 
    47     
    48 
    49 }
    50 
    51  
    52 
    53  
    54 
    55 public class TestCar {
    56 
    57      public static void main(String[] args) {
    58 
    59          Car c=new Car();
    60 
    61          c.speedUp(10);
    62 
    63          Car c1= new Car("辽B5678",80, 20);
    64 
    65          c1.speedDown(20);
    66 
    67          System.out.println(c.num + "的车速为" +c.speed + ", 载重量为" +c.weight);
    68 
    69          System.out.println(c1.num + "的车速为" +c1.speed + ", 载重量为" +c1.weight);
    70 
    71      }
    72 
    73 }

     

     

     

    5.(*)按要求编写Java应用程序。9

    (1)定义Teacher类,具有属性:教师的姓名、年龄、教授的课程以及是否担任班主任,方法printInfo输出教师的姓名、年龄和所教授的课程,构造方法为成员变量赋值。
    (2)定义Team类,有属性:班级名称和班主任,有一个输出班级名称的方法,另一个是构造方法,为成员变量赋值。
    (3)定义主类TestTeam,创建一个班级对象,输出班级名称和班主任的详细信息。

    public class Teacher {
      
         String name;
    
         String coures;
    
         int age;
    
         boolean  Tutor;
    
         public Teacher(String name, String coures, int age,boolean Tutor) {
    
             super();
    
             this.name = name;
    
             this.coures = coures;
    
             this.age = age;
    
             this.Tutor=Tutor;
    
         }
    
         public void printlnfo(){
    
             System.out.println("老师的姓名为:"+this.name+"\t\t年龄为:"+this.age+"\t教授课程为:"+this.coures);
    
         }
    
    }
    
     
    
     
    
    public class Team {
    
         String classname;
    
         Teacher tutor;
    
         public Team(String classname, Teacher tutor) {
    
             super();
    
             this.classname = classname;
    
             this.tutor = tutor;
    
         }
    
         public void className(){
    
             System.out.println("班级名称为:"+this.classname);
    
         }
    
    }
    
     
    
     
    
    public class TestTeam {
    
         public static void main(String[] args) {
    
             Teacher tea=new Teacher("薛之谦", "音乐", 33, true);
    
             Team tea1=new Team("金曲捞", tea);
    
             tea1.className();
    
             tea1.getTutor().printlnfo();
    
         }
    
    }

     

    转载于:https://www.cnblogs.com/2734156755z/p/9396422.html

    展开全文
  • 面向对象

    2011-04-20 08:09:00
    目录定义浅谈面向对象一、传统开发方法存在问题二、面向对象的基本概念三、面向对象的特征四、面向对象的要素五、面向对象的模型六、面向对象的分析七、面向对象的设计八、面向对象的实现九、面向对象和基于...
  • R语言面向对象指南

    千次阅读 2015-09-21 21:53:00
    面向对象指南:这一章主要介绍怎样识别和使用 R 语言的面向对象系统(以下简称 OO)。R 语言主要有三种 OO 系统(加上基本类型)。本指南的目的不是让你精通 R 语言的 OO,而是让你熟悉各种系统,并且能够准确地区分...
  • C++面向对象面向对象程序设计概述 会有点长,不过读过就全学会喽!!!!!! 会有点长,不过读过就全学会喽!...1.2 面向对象程序设计的基本概念面向对象的程序设计 中,着重点在那些将要被操作的数据, 而不是
  • 面向对象考题收集

    千次阅读 2017-06-10 09:29:14
    3.下列不属于面向对象技术的基本特征的是( B )。 A. 封装性 B. 模块性 C. 多态性 D. 继承性
  • 类和面向对象习题

    万次阅读 2017-04-13 21:49:31
    3.4 精选习题 ...1.下列不属于面向对象编程的特性的是( )。 A.封装 B.继承 C.抽象 D.多态 2.(*)下述概念不属于面向对象的是( )。 A.对象、消息 B.继承、多态 C.类、封装 
  • Java实现面向对象编程

    万次阅读 2018-07-17 16:18:06
    1.1用面向对象设计电子宠物系统... 14 1.1.1为什么使用面向对象... 14 1.1.2使用面向对象进行设计... 15 1.2通过创建对象实现领养宠物功能... 17 1.2.1创建类的对象... 17 1.2.2构造方法及其重载... 23 1.2.3...
  • 面向对象设计原则

    千次阅读 2014-04-09 18:10:06
    和设计模式相关的是设计原则。...面向对象设计模式描述了面向对象设计过程中、特定场景下、类与相互通信的对象之间常见的组织关系,包括它们的角色、职责、协作方式几个方面。  从编程语言来看,各种面向对象
  • 对C++面向对象的编程的理解

    千次阅读 2019-01-16 18:05:06
    本节主要介绍面向对象方法的优点以及它的一些基本概念。 2.3.1面向对象方法的优点(1)与人类习惯的思维方法一致。 长期以来,人与计算机之间仍存在着较大的隔阂,人认识问题时的认识空间和计算机处理问题时的方法...
  • 目录 第 4 章 Java面向对象程序设计 一.单项选择题 二. 填空题 三.简答题 四.程序阅读题 ...五....一....A....B....C....D....2.下列不属于面向对象编程的三个特征的是 A.封装 B.指针操作 C.多态性 D.继承 3....
  • 面向对象编程思想

    万次阅读 2014-06-24 21:18:46
    面向对象是一种新兴的程序设计方法,或者说它是一种新的程序设计范型,其基本思想是使用对象,类,继承,封装,消息等基本概念来进行程序设计。 它是从现实世界中客观存在的事物(即对象)出发来构造软件系统,并...
  • 理解面向对象

    2007-07-13 11:51:00
    为什么要学习面向对象语言?我们知道,世界上的万事万物都可以看作是属于不同类别的个体,或称之为对象,这些对象之间...面向对象概念面向对象”是围绕现实世界中的概念来组织系统模型的一种崭新的思维方式,其基
  • 面向对象设计面向对象设计一、 面向对象设计的准则(一) 模块化(二) 抽象(三) 信息隐藏(四) 弱耦合对象之间的耦合可分为两大类:(1) 交互耦合(2) 继承耦合(五)强内聚(六)可重用二、启发规则(一) 设计...
  • 面向对象软件工程知识点

    千次阅读 2016-07-11 11:46:28
    面向对象软件工程知识点  1.封装是指把对象的(A)结合在一起,组成一个独立的对象。 A.属性和操作 B.信息流 C.消息和事件 D.数据的集合 2.状态图和活动图建立了UML面向对象开发过程中的对象动态(B)...
  • 面向对象复习总结

    千次阅读 2016-12-07 21:11:27
    面向对象基本概念和原理 UML( UnifiedModeling Language )基础 面向对象设计原则 面向对象设计模式 什么是面向对象 面向对象的方法是一种分析方法、设计方法和思维方法。 面向对象方法学的出发...
  • 关于面向对象

    2011-08-21 19:31:52
    面向对象(Object Oriented,OO)是当前...面向对象概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
  • 面向对象的分析

    2014-02-22 10:48:32
    面向对象的分析 2011-04-11 08:27 300人阅读 评论(0) ...面向对象概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CA
  • 面向对象:将程序的变量以及功能整合到一起成为:对象,程序的流程是在对象生成后决定的,后期拓展程序只需要更改对象即可,理解:解决问题需要哪些对象 但并不是说开发程序一定要用面向对象编程思想来进行开发,...
  • 本篇介绍C#的面向对象三大特性 二、C#面向对象编程及三大特性 面向对象思想 什么是面向对象 -> 思考方式(面向过程是解决问题的步骤、面向对象是解决方案) -> 代理模式(找专业的人做专业的事,不是所有事都...
  • 下面关于抽象概念的描述中,正确的是 A 计算机软件开发使用的抽象有过程抽象和数据抽象两种 B 过程抽象强调功能完成的过程和步骤 C 过程抽象按照功能将整个系统的不同部分抽象成不同的类 D 数据抽象的每个抽象...
  • C++复习题(面向对象程序设计)

    千次阅读 2020-07-14 15:31:32
    下面选项中不属于面向对象程序设计特征的是( C ) 。 A)继承性 B)多态性 C)相似性 D)封装性 可用作C++语言用户标识符的一组标识符是( B )。 A) void define +WORD B) a3_b3 _123 YN C) for -abc Cas
  • Python面向对象详解

    千次阅读 2014-05-21 10:26:59
    提到面向对象,总是离开几个重要的术语:多态(Polymorphism),继承(Inheritance)和封装(Encapsulation)。Python也是一种支持OOP的动态语言,本文将简单阐述Python对面向对象的支持。 在讨论Python的OOP...
  • 严谨地说,标题应该是“汉语是一种面向对象的高级语言”。在网上看到过很多有关汉语和英语比较的文章,他们写的都很不错,并且列举了大量的事例来证明汉语或者英语是优秀的。不过,我想用计算机软件设计的观点,来...
  • 面向对象设计概念 ​ 设计是把分析阶段得到的需求转变成符合成本和质量要求的、抽象的系统实现方案的过程。从面向对象分析 到面向对象设计是一个逐渐扩充模型的过程,即面向对象设计就是用面向对象观点建立求解域...
  • 面向对象设计与分析

    千次阅读 2012-08-04 18:59:28
    面向对象设计与分析 ...面向对象概念和应用已超越了程序设计和软件开发,扩展到很宽的范围。如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,072
精华内容 6,428
关键字:

下列概念不属于面向对象