(一)面向对象和面向过程
目的:都就是为了能够重复使用代码,提高可扩展性和可维护性,避免每次都写重复性的代码。
面向过程:复用的层次,在方法上复用。
面向对象:复用的层次,在对象上复用,封装了类的内部细节和方法。
(程序的本质:对问题的一种解决方案,是思维逻辑和问题解决步骤的实现)
(程序BUG:根据程序呈现的结果,对程序实现过程进行分析,对每个环节产生的数据进行分析,找出问题所在)
(二)类和对象
类:描述同一类形对象的一个抽象的概念,类定义了这一类对象所具有的静态属性和动态属性(方法/动作)。
对象:类中的一个具体实例,有自己不同于其他对象的具体的属性和方法。
(三)类(对象)之间的关系
Unified Modeling Language (UML)又称统一建模语言,使用uml来设计类(对象)之间的关系。
其中,主要用到用例图、类图、状态图、包图、构件图、部署图等。
用例图:主要用来表示各种角色通过软件系统可以做哪些事情,即表示软件系统的绝大部分需求。其中角色可以根据组织架构。
注意:在用例图中,使用参与者和用例来表示他们之间的关系,其中参与者是在系统外部与系统直接交互的人或事物,使用小人表示。用例是系统的一个功能单元,使用椭圆表示。
类图:主要用来表示业务中的各种概念分析,即事物的属性和方法。当类图过大时,可以使用包图来分类。
状态图:主要表示业务中事物的状态变化,即事物的处理流程。
构件图:主要用来描述软件内部组成,常和部署图结合使用。
部署图:用来描述系统如何部署、本系统与其他系统是怎样的关系的一种图。
重点:类图中的类的关系:
主要包括:泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)
1、泛化(又称为继承):指定了子类如何实现父类的所有特征和行为。【箭头指向】:带三角箭头的实线,箭头指向父类;例如:老虎是动物的一种,即有老虎的特性也有动物的共性。
2、实现:是一种类与接口的关系,表示类是接口所有特征和行为的实现.【箭头指向】:带三角箭头的虚线,箭头指向接口;
3、关联:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。【箭头及指向】:带普通箭头的实心线,指向被拥有者;【代码体现】:成员变量;
4、聚合:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。【箭头及指向】:带空心菱形的实心线,菱形指向整体;【代码体现】:成员变量;
注意:聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。
5、组合:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。【箭头及指向】:带实心菱形的实线,菱形指向整体;【代码体现】:成员变量;
注意:组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。
6、依赖:是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖.【箭头及指向】:带箭头的虚线,指向被使用者;【代码表现】:局部变量、方法的参数或者对静态方法的调用;
现代人使用计算机;计算机被现代人使用。
简单说明如下:
1、关联关系:映射到程序中,一个类中的方法的参数,是另一个类的对象。
UML表示:二者之间是实线。
例如:老师教学生,老师有一个教的方法,它的参数是学生。
2、继承关系:类似XX是一种XX,则存在继承关系。(java中是单继承)
UML表示:头带三角的实线。 篮球运动员——>运动员 (三角是空心的)
3、聚合/组合关系:整体和部分
聚合:整体和部分,但是部分可以单独成为一个对象。(UML表示:头带菱形的实线,菱形是空心的):队员和球队
组合:整体和部分密不可分,部分不能单独成为一个对象。(UML表示:头带菱形的实线,菱形是实心的):脑袋和人体
4、实现关系:实现接口,子类实现方法
UML表示:带三角的虚线,三角是空心的。
5、多态
多态的必要条件:1、有继承 2、有方法的重写 3、父类引用指向子类对象(注意:父类引用不能访问子类对象相比于父类新增的属性和方法)例如:定义一个动物,在定义一个猫,父类的引用不能访问猫特有的方法和属性。
(四)面向对象的设计思维(重点)
对于问题的解决,要使用面向对象的思维
1、问题可以抽象出那些类和对象
2、这些类和对象应该具有那些属性和方法
3、这些类之间具有的关系
面向对象一个重要的设计思维:最合适的方法应该在最合适的类里面。
注意:在做数据库设计时,(可以从数据的查询和增加删除考虑表设计是否合理,能否简化SQL语句编写,通过添加字段或者表实现SQL的简化),要有高度的预见性和敏锐的洞察力:当需求改变,要从头审查整个架构设计,是否需要优化完善,修改之后能否满足日后类似的修改。
结合该博文学习理解:数据库设计建模
(五)如何分析问题中的类和对象
把问题中的事物抽象成类、属性和方法,通过合适的编程语言逻辑,把问题解决。
如何找出问题中的类,可以使用一个技巧:就是找名词。要区分好该名词是类还是属性。
1、区分类和属性的技巧:就是该名词是否有方法(动作)。
2、一般描述为xx的xx是属性。
对于类和方法的封装,要根据业务逻辑来封装,虽然可以有很多方法和属性,但是与业务逻辑无关,也无需封装。
例如:小明的身高2米,他喜欢打篮球。
分析:类有:小明和篮球 身高是属性
(六)面向对象和内存分析(重点)
这部分内容可以参考数据类型的知识内容。
成员变量:可以是java语言中的任何一种数据类型(基本类型和引用类型),成员变量只有在对象创建时,才会分配堆内存空间。
定义成员变量时,如果没有对其初始化,java会默认对其初始化。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定。
1、java语言中除基本类型之外的变量类型都可称为引用类型。
堆内存是new出来的,预先不知道其大小。在运行期间动态分配。成员变量是在堆内存中。通过引用变量(存放的是堆内存的地址)来访问堆内存中的对象和对象的成员变量。(当没有引用变量指向堆内存时,堆内存会被当做垃圾,被垃圾回收器回收,由JVM决定)。
成员变量是当对象创建时才创建,利用构造方法创建一个对象并给对象的每个成员变量赋初值(这个过程由程序员决定,即程序员决定创建哪个对象,指定对象成员变量的初值),这样才能确定其大小,然后为其分配一块内存空间即堆内存。
方法中的形参是局部变量存放在栈,值参是常量存放在数据区。
2、构造方法:用来初始化一个对象
系统会默认提供一个无参构造方法,如果自己定义了构造方法,则编译器不在提供默认的无参构造方法。需要自己来定义无参构造方法。
构造方法与类同名(大小写一致),没有返回值和返回类型。
java使用Unicode编码,每个字符占两个字节,一个字节byte是八位bit,一个字符可以通过4个16进制的数表示。
(七)方法的重写和重载
1、方法重载:方法名相同,参数不同,返回类型可以相同可以不相同,其中返回类型不能作为方法重载的判断依据。
方法重载的作用:可以避免因为参数的不同(个数,顺序,类型),而去写多个不同函数来解决问题,即一个同名函数可以实现多个功能。主要用于构造方法。
注意:可以改变方法的实现内容
2、方法重写:返回值类型和参数列表必须相同。
注意:声明为final和private的方法不能被重写
声明为static的方法,不能被重写,但是可以在子类中使用static再次声明。
例如:
public abstract class Man {
public static int play(int i,int j){
if(i>j){
return i;
}else{
return j;
}
}
}
public class Boy extends Man {
public static int play(int i,int j){ //子类可以重新声明该方法
return j;
}
//子类可以重新声明该方法
return j;
}
(八)this的用法
this可以看作是一个变量,this的值是当前对象的引用。
1、在构造方法中使用this
内存分析:在堆内存中有一块内存存放this指向当前对象的地址。
2、方法中的变量
只要是方法中定义的变量,就是局部变量,编译器自动分配一块连续的内存区域(该内存区域是栈结构),方法执行结束后,内存自动释放。
注意:栈的本质是一种数据结构。(存储方式和元素之间的逻辑关系)
(九)static和final的用法
1、static用法
static用来修饰类方法和类变量,可以直接使用类名或者对象引用来访问用static修饰的类方法和类变量。(称为静态方法和静态变量)
static修饰变量称为静态变量,在内存中存放在数据区,在使用时初始化,只有一份拷贝,被该类的所有对象共享,而非对象单独拥有。
作用:(可以用来计数,看产生了多少对象)
static修饰方法称为静态方法,静态方法不能使用非静态变量。
2、final用法
final修饰变量是常量,不能被修改。(注意:final对象的引用不能改变,但是里面的值可以改变。例如数组)
final修饰方法不能被子类重写,但是可以被子类继承。
final修饰类不能被继承。(String类,Math类,Boolean类是不能被继承的)
(十)package和import的用法
1、package用法
package 的作用就是 c++ 的 namespace 的作用,防止名字相同的类产生冲突。Java 编译器在编译时,直接根据 package 指定的信息直接将生成的 class 文件生成到对应目录下。(即:class文件必须与源文件java文件目录一样)
注意:package声明必须放在源文件的首行。命名规则:公司域名的倒写。
2、import用法
作用:就是给编译器提供一个正确的路径,使编译器能够找到该类。
同一包中的类不需要引入。
必须把class文件的最上一层包的父目录位于classpath下。
注意:配置环境变量classpath:就是为了告诉编译器去哪里找类
(十一)jdk包和打包命令
java.lang——包含一些java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
注意:使用该包的类不需要导入可以直接使用,其他包的类必须导入。
将类文件(class文件)打包即生成jar包(是压缩文件,可以使用压缩软件打开)的命令:
jar -cvf XX.jar *.*
解释说明:该命令必须在class文件的最上一层包的父目录下使用(即com的父目录中) c:表示创建新的归档文件 v:生成详细输出 f:指定归档文件名 *.*:表示把当前目录及子目录所有的东西打包到jar文件中
1、对于jar文件,可以直接将jar文件的目录设置到环境变量classpath中,就可以使用jar包中的所有类文件。
(十二)继承和访问控制符
1、对于继承构造方法,子类对象实例化的内存分析
子类在实例化一个对象时(即new一个对象),会隐式调用父类的构造方法(即子类的构造函数里第一行有一个默认的隐式语句:super())。
那是因为:子类继承了父类,获取到了父类中内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的,所以子类在构造对象时候,必须访问父类的构造函数,为了完成这个必须的动作,就在子类的构造函数中加入了super()语句。
2、子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
注意:对于父类的有参构造器,在子类构造器函数中的第一行,必须显示的通过super关键字调用并配以适当的参数列表。
注意:super是指向父类对象的引用,指向父类对象堆内存的地址。同this区分开。
default:只可以被同一包中的类访问。(default就是默认省略的)
protected:可以被同一包中的类访问,同时,他的子类也可以访问(即使子类在不同的包中也可以)。
(十三)Object类(重点)
Object类是所有类的父类。对于一些方法的参数类型都是Object类型,是合理的,采用的多态,父类引用指向子类对象。
1、toString()方法:对于输出中+(加号)连接字符串时,对于引用类型系统会自动调用该对象类的toString()方法,将其转化为字符串输出。对于基础类型,调用基础数据类型的封装类的toString()方法。
其中:Object类的通toString()方法,返回形式为: getClass().getName() + '@' + Integer.toHexString(hashCode())
对于该方法如果不满意,可以重写该方法。
java虚拟机(JVM)通过hashcode(哈希编码)获取内存中的对象。类似于Map
2、equals()方法(对象引用比较的是地址而不是内容,此时equals等价于==)
当对象引用调用equals()方法时,如果两个变量引用的是同一对象,方法返回true。但是对于同一个类的两个对象,即使参数一样,调用equals()方法,返回false。
例如:Man a = new Man();Man b = new Man(); a.equals(b) 结果返回false 因为引用a,b是两个不同的内存地址。
因此,一般需要重写该方法,注意:instanceof 关键字的使用,instanceof:表示测试一个对象是否是某个类的实例。
格式: 引用变量 instanceof 类名
3、hashcode()方法
注意:该方法java实现的不是很好,一般重写了equals()方法,就有必要重写hashcode()方法。
一般是通过将该对象的内部地址转换成一个整数来实现的。
对于equals()和hashcode()方法注意事项:
hashcode()表示的是JVM虚拟机为这个Object对象分配的一个int类型的数值,JVM会使用对象的hashcode值来提高对HashMap、Hashtable哈希表存取对象的使用效率。
结论:在设计hashCode方法和equals方法的时候,如果对象中的数据易变,则最好在equals方法和hashCode方法中不要依赖于该字段。
重写equals()方法和hashcode()方法实例:
重写的目的是为了保证: equals
与hashCode
的定义必须一致,两个对象equals
为true
,就必须有相同的hashCode
。(原来对象的equals方法比较的是地址,重写之后比较的是内容,而hashcode方法还是返回原来的地址整数值,即对象equals相同,而hashcode值不同,因此要重写hashcode方法)
- 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用
hashCode
方法都必须生成相同的整数结果。
对于自定义类,要根据需要重写父类Object的equals()方法,就必须重写hashcode()方法
- Public int hashCode()
- {
- return name.hashCode() + age * 10;//表达式结果要根据equals方法比较的内容生成hashcode值。 即字符串的hashcode+int类型的值即可,保证了两个方法的一致性。
- }
- public boolean equals(Object obj)
- {
- if(!(obj instanceof Person))
- throw new ClassCastException("类型不匹配");
- Person p = (Person)obj; //这个强制转换可有可有,因为obj已经判断是Person的实例了
- returnthis.name.equals(p.getName()) && this.age == p.getAge();
- }
(十四)多态(重点)
多态又称为动态绑定:是指在执行期间(而非编译期间)判断引用对象的实际类型,根据其实际类型调用其相应的方法。(在内存中的体现就是:生成的堆内存中有一个指针在执行期间动态的指向了重写了父类方法的子类方法)
作用:父类引用指向子类对象,更好的提高了程序的可扩展性。(可以更换不同的子类对象,同时,父类引用无法访问子类对象特有的方法和属性)
注意:抽象类和接口都具有多态性,即父类引用指向子类对象。
(多态重点)
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
主要有两点:
1、父类引用指向子类对象时,引用在调用方法时,如果是子类和父类都有的方法,优先调用子类重写了父类的该方法,即方法指向的是子类重写的方法而不是父类的方法
2、a2.show(c),a2是A类型的引用变量,所以this就代表了A,a2.show(c),它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级,也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。
参考:多态详解
(十五)接口
接口本质:就是一种特殊的抽象类。
接口中的变量必须是静态常量,即public static final 类型。这是为了解决接口多继承同名变量可能引起的未知问题,类似于C++中多继承的同名变量问题。定义成public static final 这样变量不在属于接口自身。
1、多个无关的类可以实现同一个接口
2、一个类可以实现多个无关的接口
3、接口和实现类之间存在多态性
4、接口之间可以多继承
注意:一个类实现多个接口时,接口中具有同名方法。解决冲突方法:java规范里,方法名+参数(不含返回值类型)唯一确定一个方法。即定义不同的参数,来解决同名方法问题。