-
学习面向对象内容的三条主线
1.java类及类的成员(五成员)
2.面向对象的三大特征
3.其它关键字
主要内容:
1 面向对象与面向过程
2 java语言的基本元素:类和对象
3 类的成员之一:属 性
4 类的成员之二:方 法
5 对象的创建和使用
6 再谈方法
7 面向对象特征之一:封装和隐藏
8 类的成员之三:构造器(构造方法)
9 几个关键字:this、package、import
1 面向对象特征之二:继承
2 方法的重写(override)
3 四种访问权限修饰符
4 关键字super
5 子类对象实例化过程
6 面向对象特征之三:多态
7 Object类、包装类
1 关键字:static
类变量、类方法
单例(Singleton)设计模式
2 理解main方法的语法
3 类的成员之四:初始化块
4 关键字:final
5 抽象类(abstract class)
模板方法设计模式(TemplateMethod)
6 更彻底的抽象:接口(interface)
工厂方法(FactoryMethod)和代理模式(Proxy)
7 类的成员之五:内部类
[]面向对象(OOP)与面向过程
二者都是一种思想,面向对象是相对于面向过程而言的。
面向过程,强调的是功能行为。
* 面向对象,将功能封装进对象,强调具备了功能的对象。
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
[]面向对象的思想概述
程序员从执行者转化成了指挥者。
完成需求时:
先去找具有所需功能的对象来用。
如果该对象不存在,那么创建一个具有所需功能的对象。
这样简化开发并提高复用。
* 类(class)和对象(object)是面向对象的核心概念。
* 类 是对一类事物描述,是抽象的、概念上的定义
对象 是实际存在的该类事物的每个个体,因而也称实例(instance)。
面向对象的三大特征
封装 (Encapsulation)
继承 (Inheritance)
多态 (Polymorphism)
OOP: Object Oriented Programming
面向过程:procedure oriented programming
--------------------------------
[]类与类之间的关系:
关联,继承,聚集,组合(聚合)
public class Customer1 {
private Account account;
}
关联关系,将类与类联系起来,建立了关系 顾客与银行账户,教授和研究生
继承关系,父类与子类,运动员与篮球运动员
聚集关系,球队有队长和球员
组合关系,人有头,手等组合起来 ,人有自己独有的头,而队长可以是两队的队长,比聚集关系强,与上统称聚合关系
面向对象程序设计的重点是类的设计
定义类其实是定义类中的成员(成员变量和成员方法)
思想法则一:
设计类:设计类的成员(成员变量-属性,成员方法-函数)
通过类来创建对象(类的实例化)
通过对象.属性或对象.方法...来调用方法,完成相应的功能
[]类的属性(成员变量)
在方法体外,类体内声明的变量称为成员变量。声明在类里,方法外,有初始化值默认值
在方法体内部声明的变量称为局部变量。声明在方法内或形参,代码块内
同:一样的声明格式,都有一定的作用域与生命周期 。
*** 异:局部变量除形参外需显式初始化(没有默认初始化值),没有独自的修饰符,与所在方法修饰符相同。
在内存存放的位置不同:局部变量在栈空间中,成员变量在堆空间中。
在方法内,int i;age+=i;一起写会报错,局部变量需要显示初始化才能使用,没有默认初始化值
[]类的方法:提供功能的实现
* 方法是类或对象行为特征的抽象,也称为函数
权限修饰符+返回值类型 方法名(形参){有返回值的话,方法里有return}
方法里可以调用本类的其他方法或属性,不能再定义方法
类里不允许出现除了方法和属性的语句;
-
方法的重载overload 与方法重写override区别开来
在同一个类中,方法名必须相同,方法的参数列表不同(形参名可以不同,类型顺序,个数要一致)
考虑对象调用方法时会不会出现不知道调用哪个方法的情况。如果不会则可以构成重载,反之。
与返回值类型,方法修饰符无关,只看方法名与后边
为了可以再定义一个具体的相同方法名的方法,根据传入的参数不同
-
匿名类对象
创建的类的对象是匿名,没有名字的
当我们只需要一次调用这个对象时,可以创建,它只能被调用一次,没有引用名
-
可变个数形参
//下面采用数组形参来定义方法
public static void test(int a ,String[] books);
//以可变个数形参来定义方法
public static void test(int a ,String…books);
1.可变参数:方法参数部分指定类型的参数个数是可变多个,0~多个
2.声明方式:方法名(参数的类型名...参数名)
* 3.可变参数方法的使用与方法参数部分使用数组是一致的,在方法体内使用和数组一致,
导致不可以与形参是数组,类型相同的构成重载。
*** 4.方法的参数部分有可变形参,必须放在形参声明的最后
可以与同名方法之间构成重载,默认优先调用本身有形参的
public void print(String str){
}
public void print(String...str){
方法内与数组调用一直str[0]表示第一个形参
} 包含关系构成重载方法,不会报错,默认调用有形参的,如果不能匹配再调用可变参数的方法
public void print(String[] str){
} 报错,方法重复 ,不能与数组参数构成重载
-
方法的参数传递
方法,必须有其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
Java的实参值如何传入方法呢?
* Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型的:将实参的值传递给形参的基本数据类型的变量
引用数据类型的:将实参的引用类型的变量的值(对应对空间的对象实体的地址值)传递给形参的引用类型变量
*** copy的是地址值,不会多在堆空间多new出来对象传递,传递的是地址值
通俗的说形参传递进方法里,在方法里的操作,不会影响在方法外的参数值。但是可以根据地址值修改相应内存空间的内容
因为形参传递的是实际参数的副本
例如swap(int i,int j){} 与swap(arr[],i,j)不同
class Value {
int i = 15;
}
class Test {
public static void main(String argv[]) {
Test t = new Test();
t.first(); //
}
public void first() {
int i = 5;
Value v = new Value(); i=15
v.i = 25; i=25
second(v, i); 25 5
System.out.println(v.i);
}
public void second(Value v, int i) {
i = 0;
v.i = 20; 20
Value val = new Value();
v = val; 15
System.out.println(v.i + " " + i); 15+0
}
}
[]类的访问机制:
在一个类中的访问机制:类中的方法可以直接访问类中的成员变量与方法。(例外:static方法访问非static,编译不通过。)
在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员。
类的初始化的内存解析
Person p1=new Person();
p1.setAge(10);
Person p2=p1;
p1=null; 此时p1的指向地址为null,
相当于只进行了声明还未初始化Person p1; 如果p1.getAge()会出现空指针异常
System.out.println(p2.getAge()); 10
创建一个类的多个对象,他们彼此拥有各自的一套属性,当对其中一个值修改不会影响其他的,属性有默认初始化值
除非对象间使用=,把一个对象的地址赋给另一个对象,此时他们共用一套属性,而不意味着重新创建一个对象。
内存划分
栈:局部变量,对象引用名,数组引用名
堆:new出来的(对象或数组的实体) ,实体内包含成员变量
方法区:字符串常量
静态域:静态的方法与变量
[]特性之一:封装(与隐藏)
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
-
特性的作用,目的,解决什么问题,如何解决?
* 当创建类的对象后,类里声明属性并不能对属性添加限制等(而方法可以对属性进行限制,方法里可以实现一些功能),
如果直接通过"对象.属性"的方式对属性赋值,可能会出现不满足实际情况的属性出现,不能控制
于是,不让对象直接来调用属性,实际情况对属性的要求,限制可以写在方法里,只能通过方法来调用属性体现封装性。
解决:(封装性的思想)将类的属性私有化,提供公共的方法来调用。
属性权限修饰符修饰为private,提供属性的get,set方法
Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,好处:
1.隐藏一个类中不需要对外提供的实现细节和信息;良好的封装能够减少耦合;
2.使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作,对属性进行精确控制;保证了程序的安全性;
3.便于修改,修改类内部的结构等,增强代码的可维护性;
如果不想被外界方法,我们可不必提供方法给外界访问,就能隐藏属性
对属性进行精确控制
-
权限修饰符:
可以修饰属性和方法,部分可以修饰类,置于类的成员定义前,用来限定对象对该类成员的访问权限。
类内部 同一个包 子类 任何地方
private yes
缺省(不写) yes yes
protected yes yes yes
public yes yes yes yes
*子类通过在类里导入父类的包可以继承另一个包的父类来调用父类的方法或属性
*对于class的权限修饰只可以用public和default(缺省)。
public类可以在任意地方被访问。
default类只可以被同一个包内部的类访问。
[]类的成员三:构造器(构造方法)
构造器的特征:
它具有与类相同的名称, 也属于特殊的方法
它不声明返回值类型。(与声明为void不同)
不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值
构造器的作用:1.创建对象;2.给对象进行初始化
* 类对象的属性初始化赋值的先后顺序,后边会覆盖前边的值
1.属性的默认初始化
2.属性的显示赋值
3.通过构造器给属性初始化
4.对象.方法给属性赋值
代码块知识有具体的执行顺序
根据参数不同,构造器可以分为如下两类:
隐式无参构造器(系统默认提供)
显式定义一个或多个构造器(无参、有参)
注 意:
Java语言中,每个类都至少有一个构造器(默认提供一个空参的构造器)
* 默认构造器的修饰符与所属类的修饰符一致,构造器修饰符使用与方法一样
* 一旦显式定义了构造器,则系统不再提供默认构造器
* 一个类可以创建多个重载的构造器
* 父类的构造器不可被子类继承
*** 子类中所有的构造器默认都会访问父类中空参构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
[]this关键字
它在方法内部使用,即这个方法所属对象的引用;
它在构造器内部使用,表示该构造器正在初始化的对象。
this表示当前对象,可以调用类的属性、方法和构造器
什么时候使用this关键字呢?
当在方法内需要用到调用该方法的对象时,就用this。
* 1.当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
2.在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
this变量名不重名时可以省略
* 3.this可以作为一个类中,构造器相互调用的特殊格式
this(name) ; // 调用有一个参数的构造方法 注意小区别调用方法
this(); // 调用本类中的无参构造方法
this.方法名()是调用方法 this.属性调用属性
注意:
1.使用this()必须放在构造器的首行! 原因
2.使用this调用本类中其他的构造器,保证至少有一个构造器是不用this的。
[]JavaBean
JavaBean是一种Java语言写成的可重用组件。
所谓javaBean,是指符合如下标准的Java类都可以叫做JavaBean:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用java代码创造的对象进行打包,
并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。
用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
UML图:
+public -private #protected
[]关键字:
package
Java源文件的第一条语句,指明该文件中定义的类所在的包
.,表示一个目录文件夹层次,包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次
import
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。import语句告诉编译器到哪里去寻找类
显示导入指定包下的类和接口
默认的java.lang包下的类不需要显示的声明包括System,String,Math等类
*** .*表示导入包下的所有类与接口,不会导入包下子包的类与接口
当一个类以有不同名包的重名类,必须有一个显示的写全类名来调用
例如:Java.sql.Date d;
静态导入 import static 表示导入指定类的static属性或方法,就可以在其他类直接使用
1.java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能。
2.java.net----包含执行与网络相关的操作的类和接口。
3.java.io ----包含能提供多种输入/输出功能的类。
4.java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
5.java.text----包含了一些java格式化相关的类
6.java.sql----包含了java进行JDBC数据库编程的相关类/接口
7.java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
8.java.applet----包含applet运行所需的一些类。
[]面向对象特征之二:继承
为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类subClass),单独的这个类称为父类(基类或超类superClass)。
class Subclass extends Superclass{ }
作用:
继承的出现提高了代码的复用性。
*** 继承的出现让类与类之间产生了关系,提供了多态的前提。
不要仅为了获取其他类中某个功能而去继承
子类继承了父类,就继承了父类的方法和属性(不能继承构造方法)。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
关于继承的规则:
*** 当父类中有私有方法或属性时,子类同样可以获取,只是由于封装性子类不能直接访问父类中私有的(private)的成员变量和方法。
Java只支持单继承,不允许多重继承,继承的传递性:可以多层继承(子类继承-父类1继承-父类2). 父类1也叫子类的直接父类,父类2叫间接父类,子类拥有父类1和父类2的结构
一个子类只能有一个父类
一个父类可以派生出多个子类
class SubDemo extends Demo{ } //ok
class SubDemo extends Demo1,Demo2...//error
子类除了通过继承获取到父类的结构,还可以拥有自己特有的成分,子类是对父类的扩展。
子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量使用this,super来区分
-------------------------------------
方法重写
针对于子类继承父类后,若父类的方法不适用可以如下:
对父类同名方法的重写(override,overwrite),覆盖,子类造的对象调用的则是子类的本方法
规则:
子类方法的 “返回值类型" “方法名” “(参数列表)" 与父类方法一致
子类方法的修饰符不能小于父类方法的修饰符
若父类方法抛异常,子类方法抛的异常类型不能大于父类
子父类的方法必须同为static或非static
如果父类有方法是private的,子类不能访问,子类写同名的方法,不算重写。可以自己定义这个方法
算是子类特有的方法,属性也一样可以使用super.属性或方法获取父类同名的属性和方法
[]this关键字
它在方法内部使用,即这个方法所属对象的引用;
它在构造器内部使用,表示该构造器正在初始化的对象。
this表示当前对象,可以调用类的属性、方法和构造器
什么时候使用this关键字呢?
当在方法内需要用到调用该方法的对象时,就用this。
* 1.当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
2.在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
this变量名不重名时可以省略
* 3.this可以作为一个类中,构造器相互调用的特殊格式
this(name) ; // 调用有一个参数的构造方法 注意小区别调用方法
this(); // 调用本类中的无参构造方法
this.方法名()是调用方法 this.属性调用属性
注意:
1.使用this()必须放在构造器的首行!
2.使用this调用本类中其他的构造器,保证至少有一个构造器是不用this的。
*** 3.使用this调本类的属性,如果没有从父类继续查找
[]super关键字
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造方法中调用父类的构造器,必须放在子类构造器首行!
注意:
*** 尤其当子父类出现同名成员时,可以用super进行区分
super的追溯不仅限于直接父类,间接父类也可以调用
super和this的用法相像,this可以特殊的代表本类对象的引用,super代表父类的内存空间的标识
*** 子类中所有的构造器(无论是否带参)默认都会访问父类中"空参数"的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
子类对象的实例化过程
会默认调用父类空参构造器直接Object类,会先执行父类构造器里的语句
----------------------------------------------------
[]面向对象特征之三:多态 晚绑定,运行时,一个接口,多种实现
多态性指的是什么,体现在?
理解为一个对象的多种表现形式,引用变量有两个类型:编译时类型和运行时类型,两个类型不一致时出现多态;多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定
发送消息给某个对象,让该对象自行决定响应何种行为
Java实现多态有三个必要条件:继承、重写、向上转型(父类引用指向子类对象)
***子类对象的多态性使用前提:
要有类的继承,和子类对父类方法的重写,父类引用指向子类对象
多态性,是面向对象中最重要的概念,在java中有两种体现:
方法的重载(overload)和重写(overwrite)。
子类对象的多态性-->可以直接应用在抽象类和接口上。
Java中多态的实现方式:接口实现(一个接口多种实现),继承父类进行方法重写(虚拟方法调用),同一个类中进行方法重载。
小tips:
重载式多态,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。可以理解为同一行为具有多种表现形式,也是多态的一种,也有人否定
应用: 传递参数声明时是父类,具体使用时可以是具体间接或直接继承父类的某一个子类对象。
父类类型的引用指向子类的对象或者实体,虚拟方法的调用
*** 子类对象的多态性,并不适用于属性,不存在覆盖属性。 成员变量:不具备多态性,只看引用变量所属的类。通常获取属性调用get,set方法来实现
父类类型的引用指向子类的对象或者实体(自动向上转型,不需要显示指定)
Person p2=new Women()
向下类型转换(Downcast):将父类型转换为子类型。 必须强制类型转换 //向下类型转换
并且父类型的引用必须指向子类的对象,即指向谁才能转换成谁。不能是将父类转换为另外一个子类
-
Animal a = new Cat();
-
Cat c = (Cat)a;
-
c.sing();
-
c.eat();
虚拟方法调用:通过父类的引用指向子类的对象实体当调用方法时,实际执行的是子类重写父类的方法
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下)
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型
实现多态的技术-动态绑定--->编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。
实现重写多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
*** p2不能调用父类没有(子类特有的方法),只能调用子类重写父类的方法,实际也是这样调用的。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中特有的的属性和方法,
*** 只能调用重写父类的方法,调用的重名属性是父类的属性,即使父类的属性是私有的,也不能访问到子类的特有属性或重名属性,也就表明了属性没有多态
* 变量只能有一种确定的数据类型
* 引用变量可能指向(引用)多种不同类型的对象
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。若编译时类型和运行时类型不一致,就出现多态(Polymorphism)
对于多态来说,编译时,看左边,将引用理解为父类的类型 父类里没有相应的方法名则编译错误 左边的引用变量
* 运行时,看右边,关注于真正对象的实体,子类的对象,实际执行的方法就是子类里重写的方法 右边的new的对象实例
static方法的多态?
使用对象调用静态方法只看左边的引用是什么类
静态static方法属于特殊情况,静态方法只能继承,不能重写Override,如果子类中定义了同名同形式的静态方法,它对父类方法只起到隐藏的作用。调用的时候用谁的引用,则调用谁的版本
多态的作用:消除类型之间的耦合关系
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要
[]对象类型转换 (Casting)
对Java对象的强制类型转换称为造型
从子类到父类的类型转换可以自动进行 向上
从父类到子类的类型转换必须通过造型(强制类型转换)实现 向下
无继承关系的引用类型间的转换是非法的
在造型前可以使用instanceof操作符测试一个对象的类型
关键字instanceof:对象 instanceof类:判断对象a是否是类A的一个实例,
// 是的返回的true,a是A类的实例,那么a也一定是A类的父类的实例
// 任何的对象都是Object的实例
Person p2 = new Woman(); 将子类Woman的对象实体赋给父类的引用变量,为向上转型(自动)
Man w = (Man) p2; 编译不会出错,运行会报类型转换错误
Person p1 = new Man();
if (p1 instanceof Man) { 为保证不会出现转换错误,转换前先判断
Man m1 = (Man) p1; //向下转型
m1.entertaiment();
System.out.println("aba");
}
判断父类对象p1是不是子类Man的实例,向下转型需要insanceof判断,并且需要强转
----------------------------------------------------------------
[]Object类:
默认是所有java类的根父类
Object类的方法:
public Object()
public boolean equals(Object obj)
public int hashCode()
public String toString()
getClass()
wait()
notify() notifyAll()
public boolean equals(Object obj)与"==":
"=="
基本数据类型:判断值是否相等,不拘泥于基本数据类型
引用数据类型:判断内存地址是否相等
String内存解析:
比较字符串(创建字符串有两种形式,存在着差别):
String s1="ABC"; String s2="ABC" s1==s2 //true
因为内存是先去字符串常量池找"ABC"没有找到则将字符串存入进去,并且s1指向这个地址
如果发现已经有"ABC"则直接指向常量池的地址
String s3=new String("ABC"); s1==s3 //false不管常量池有没有相同的内容的引用,都会在对空间中创建一个新的对象。对象的地址不同,但是对象里的字符串也是指向常量池的
调用interm方法后,会将这个对象的引用加入到字符串常量池
String s4=s3.intern() s4==s1 //true
equals(Object obj)只能比较引用类型变量
Object的equal方法比较的是this==obj 仍然是地址值
特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;
原因:在这些类中重写了Object类的equals()方法。
toString()方法
toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。 类名@对象实体的首地址值(16进制的hashcode值)
return getClass().getName+"@"+Integer.toHexString(hashCode);
在进行String与其它类型数据的连接操作时,自动调用toString()方法,当对象所在类没有重写方法时,调用的就是以上Object定义的toString方法
Date now=new Date();
System.out.println(“now=”+now); 相当于
System.out.println(“now=”+now.toString());
可以根据需要在用户自定义类型中重写toString()方法
如String 类重写了toString()方法,返回字符串的值。常常是返回对象属性的值,或字符串
s1=“hello”;
System.out.println(s1);//相当于System.out.println(s1.toString());
Date,String类,包装类,File类等也都重写了toString方法
基本类型数据转换为String类型时,调用了对应包装类的toString()方法
int a=10; System.out.println(“a=”+a);
[]包装类:
针对八种基本定义相应的引用类型—包装类(封装类)可以取值为null,默认值是null看成引用类型
boolean Boolean
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
基本数据类型包装成包装类的实例 ---装箱
*** 通过包装类的构造器实现: 基本数据类型不能直接调用方法,需要包装成包装类。
int i = 500; Integer t = new Integer(i);
Integer i=new Integer(123);
*** 还可以通过字符串参数构造包装类对象:前提是字符串的类型也必须匹配
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException
Integer i=new Integer("123");
*** Boolean 装箱 ("") 括号里边true为true 其他的不会报错结果都是false
获得包装类对象中包装的基本类型变量 ---拆箱
调用包装类的.xxxValue()方法:
boolean b = bObj.booleanValue();
JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
Integer i=new Integer(12);正常的写法
自动装箱:Integer i=12; 直接将基本数据类型赋值给包装类,会进行自动装箱操作
int i1=i.intValue(); 正常
自动拆箱 int i1=i;
基本数据类型:
转化为包装类: 自动装箱
构造器包装传递值或字符串参数,类型必须匹配
String类: 调用String类的静态方法valueOf(3.4f)或3.4f+""
包装类:
转化为基本数据类型: 自动拆箱
调用包装类的xxxValue();转化为xxx对应数据类型,可以强转
String类 :调用toS 或包装类的静态方法toString直接转换
String类:
转化为基本数据类型:调用包装类的静态方法parseXXX(String)或通过包装类构造器boolean b=new Boolean("true")自动拆箱
转化为包装类:通过字符串参数构造器
利用Vector类代替数组,数组一旦创建长度不变,Vector可以动态伸缩
Vector v=new Vector();
添加元素v.addElement(obj), obj必须是对象 可以放入基本数据类型会进行自动装箱
取出元素 Objec obj=v.elementAt(0);第一个元素index为0
计算长度v.size();
[]关键字static
静态的,可以用来修饰属性,方法,代码块(初始化块)
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。
我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,并且可以不用创建对象来直接调用
static修饰:
类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
由类创建的所有对象,全局唯一(共享)都共用这一个属性
* 1.当一个 对象对此属性进行修改,会导致所有的对象对此属性调用
* 实例变量:是分别调用自己的属性,可以对自己的属性分别修改
* 2.类变量是随着类的加载而加载,而且只有一份
* 3.类变量的加载是要早于对象的,所有可以通过类.类变量的形式来调用,不用创建对象就可以调用
* 实例变量是new出来随着对象的创建而被加载的,不可以通过类.调用
* 4.内存分析:存在单独的静态域
如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用
* 随类的加载而加载,内存独一份,可以通过类.类方法来调用
* 1.静态方法只能够调用静态属性,方法等,而不能调用非静态的 因为当调用的非静态方法里有对象调用时就会出错,因为对象还没产生
* 反过来非静态的能调用静态的,晚产生的可以调用已存在的
*
* 静态的结构(static的属性等生命周期早于非静态,同时回收也晚)
静态的可以调用静态的并且可以直接调用不可以使用this,不可以调用非静态的
非静态方法可以调用静态的属性或方法
*** 因为不需要实例就可以访问static方法,因此static方法内部不能有this。(也不能有super ? YES!)
重载的方法需要同时为static的或者非static的。
***如果父类中有一个静态的方法,子类也有一个与其方法名,参数类型,参数个数都一样的方法,并且也有static关键字修饰,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。通俗的讲就是父类的方法和子类的方法是两个没有关系的方法,具体调用哪一个方法是看是哪个对象的引用;这种父子类方法也不在存在多态的性质
静态的方法不能覆写,也不能被重写。总之,静态的没有重写,可以被继承
单例(Singleton)设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。
因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
一个类只能创建一个对象:
1.私有化构造器
2.在类的内部创建私有的static本类的唯一对象,作为属性,static供static方法调用
3.创建一个公共的返回对象的static方法
饿汉式: java.lang.Runtime类就是单例模式的例子
class Single{
//private的构造器,不能在类的外部创建该类的对象
private Single() {}
//私有的,只能在类的内部访问
private static Single onlyone = new Single();
//getSingle()为static,不用创建对象即可访问
public static Single getSingle() {
return onlyone;
}
}
public class TestSingle{
public static void main(String args[]) {
Single s1 = Single.getSingle(); //访问静态方法
Single s2 = Single.getSingle();
if (s1==s2){
System.out.println("s1 is equals to s2!");
}
}
}
懒汉式:可能存在线程安全问题
class Singleton1{
private static Singleton1 instance=null;
private Singleton1(){
}
public static Singleton1 getInstance(){
if(instance==null){
instance=new Singleton1();
} //创建再返回
return instance;
}
}
理解main方法:
由于java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。
传递的是可变参数类型,args[0]代表第一个参数
运行程序Test.java
java Test "lisa" "bily" "Mr Brown" 后边三个为传递的参数
类的成员之四:初始化块(代码块)
初始化块(代码块)作用:
{ 或 static{
对Java对象进行初始化
} }
静态代码块(static block )
*** 一个类中初始化块若有修饰符,则只能被static修饰,当类被载入时,类属性的声明和静态代码块按先后顺序被执行,且只被执行一次。
static块通常用于初始化static (类)属性
非静态代码块:没有static修饰的代码块
1.可以有输出语句。
2.可以对类的属性、类的声明进行初始化操作。
3.可以调用静态的变量或方法。
4.若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
5.每次创建对象的时候,都会执行一次。且先于构造器执行
静态代码块:用static 修饰的代码块
1.可以有输出语句。
2.可以对类属性、类声明进行初始化操作。
3.不可以对非静态的属性初始化。即:不可以调用非静态的属
性和方法。
4.若有多个静态的代码块,那么按照从上到下的顺序依次执行。
5.静态代码块的执行要先于非静态代码块。
6.静态代码块只执行一次
相同点:静态代码块和非静态代码块都是在JVM加载类时且在构造器之前执行
不同点:静态代码块在非静态代码块之前执行,静态代码块只在第一次被new时执行之后不再执行,非静态代码块new一次执行一次
类的加载和初始化:
每运行一次程序,会对类进行加载和初始化
类通过装载,连接,初始化才可以被使用
1.首先加载创建对象的类及其直接或间接父类
2.然后在上面的类被加载时进行初始化,完成静态成员的加载,静态块的执行,按代码先后顺序执行
3.当类加载完成后,会开始创建对象,属性默认初始化->完成非静态成员变量的初始化,非静态代码块的执行,按代码先后顺序执行
4.最后执行构造器,生成对象
类通过类加载器加载完后就开始初始化Test类:初始化静态变量,静态块(按代码的先后顺序),然后classLoder返还这个类,调用Test类的静态main方法,然后遇到其他类再加载其他类及其父类
new对象时,实例变量,非静态代码块(按代码的先后顺序)->构造方法
当有继承时,也是基于上面的原理
java类实现继承后程序的执行顺序
父类静态属性或静态代码块
子类静态属性或静态代码块
父类非静态属性或非静态代码块
父类无参构造器
子类非静态属性或非静态代码块 按照上下顺序来决定
子类无参构造器
如果有无参构造器显示调用父类带参构造器,会先执行父类无参构造器,再执行父类带参构造器
Root父类->Mid->Leaf子类
new Leaf();
System.out.println();
new Leaf();
//new Leaf()会调用Leaf的空参构造器,同时加载Leaf这个类与直接间接父类,完成初始化
然后调用到父类的构造器,之前加载非静态代码块,然后执行父类构造器,再子类构造器
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
执行Leaf的构造器
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
执行Leaf的构造器
程序如何运行的,类什么时候会执行代码?
程序是一个类,static main方法是程序的入口
先加载这个类,加载这个类的静态东西,类加载器返回这个类再调用main方法,遇到new等再加载其他类
静态代码块是在程序一运行就加载还是第一次new时加载?
类加载,在类第一次被调用时或第一次new时会加载类
[]关键字final
在Java中声明类、属性和方法时,可使用关键字final来修饰,表示“最终”。
final标记的类不能被继承。提高安全性,提高程序的可读性。
例如:String类、System类、StringBuffer类,StringBuilder类
final标记的方法不能被子类重写。
Object类中的getClass()。
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次,不能再被修改值,赋值,会报错。
*** final标记的成员变量必须在声明的同时或在每个构造方法中或代码块中"显式赋值"(在创建对象前"必须"赋值,可以使用构造器传入的形参赋值),不能使用默认初始化值,然后才能使用。
final double PI=3.14;
public int addOne(final int x){
不能再对x修改 x=10; 报错
直接return x; 对
}
--------
public void addOne(final Other o){
o.i++ //final修饰的是Other的一个对象o,指向的对象o是不变的就可以
o=null;或o=new Other();就会报错
}
class Other{
public int i;
}
static final:全局常量,可以直接由类.属性调用常量
finally :try catch语句最后一定要执行的语句
finalize() :垃圾回收用到,显示的清理对应堆空间
-----------------------------------------------------------------------------
[]抽象类:
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用类的设计应该保证父类和子类能够共享特征。
有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
用abstract关键字来修饰一个类时,这个类叫做抽象类;abstract声明在class前 权限修饰符后
用abstract来修饰一个方法时,该方法叫做抽象方法。
*** 抽象方法:只有方法的声明,没有方法的实现。
以分号结束:abstract int abstractMethod( int a );格式必须一样没有{}
含有抽象方法的类必须被声明为抽象类。可以有其他完整的方法
即抽象方法所在的类一定是抽象类
*** 抽象类:不能被实例化。但是可以定义构造器,子类有可能调用(凡是类都有构造器)-接口没有构造器
不能被实例化,方法也就不能调用,所以可以写成抽象方法。也可以有具体实现的方法存在是混合的
抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。
可以使用多态,所以抽象类的抽象方法虽然没有实现,但是要写。
*** 继承后,若没有重写全部的抽象方法,可以不实现所有的抽象方法,但是类也需要声明为抽象类。
*** 不能用abstract修饰属性、私有方法、构造器、静态方法、final,的方法。
属性不能像方法一样覆盖所以没有抽象这种思想
构造器也不能被重写
私有方法,不认为子类能重写父类的private方法
final方法定义也不能被重写
static方法,类方法通过类来调用一个不完整的方法没有意义
可以修饰protected和默认修饰符的方法
protected abstract void test1();
abstract void test2();
模版方法的设计模式:
抽象类体现的就是一种模板模式的设计,
抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
[]接口:
有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
接口(interface)是抽象方法和常量值的定义的集合。仅定义功能,可以被类具体实现
*** 从本质上讲,接口是一种特殊的抽象类。与类是并行的概念,不算做类。
*** 接口中只包含常量和抽象方法的定义,而没有变量和方法的实现。
没有构造器
interface AA{
int I=12;也是常量 ,都默认用public static final修饰 只能这样修饰
抽象方法
void method1(); 也是抽象方法,都默认用public abstract修饰,且只能是public abstract 不写public也默认是public
}
实现接口类:多实现,单继承
class SubClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口。(面向接口编程)
定义Java类的语法格式:先写extends,后写implements
实现接口必须实现接口所有的抽象方法,或定义抽象类实现接口的一部分方法但不能实例化这个抽象类
接口与接口之间仍是继承关系(可以多继承)
接口与类之间是实现
接口与具体的实现类之间也有多态性
接口用法总结:
通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。
通过接口可以指明多个类需要实现的方法,一般用于定义对象的扩张功能。
接口主要用来定义规范。解除耦合关系。
区别:
如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限
一个抽象类中可以包含多个接口,一个接口中可以包含多个抽象类
接口不能继承抽象类,但允许继承多个接口,抽象类可以实现多个接口
[]抽象类和接口的区别
1.语法层面上的区别
1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
3)接口中不能含有静态代码块以及静态方法,构造器,而抽象类可以有静态代码块和静态方法,构造器;
4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2.设计层面上的区别
1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计,想想计算机接口。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。
[]类的成员指五:内部类
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
Inner class的名字不能与包含它的类名相同;
Inner class可以使用外部类的私有数据,因为它是外部类的成员,同一个类的成员之间可相互访问。
内部类中的this指的是内部类的实例对象本身,如果要用外部类的实例对象就可以用外部类名.this的方式获得
*** 而外部类要访问内部类中的成员需要:内部类.成员或者内部类对象.成员。
在内部类里调用
System.out.println(s); // 局部变量s
System.out.println(this.s); // 内部类对象的属性s
System.out.println(A.this.s); // 外层类对象属性s
public static void main(String args[]){
A a = new A();
A.B b = a.new B(); 如何创建非静态内部类的对象,需要两步,必须先创建外部类的对象
A.B b = new A.B(); 静态内部类B
//可以 Inner inner = new Outer().new Inner()
//或Outer outer = new Outer(); Inner inner = outer.new Inner()
}
分类:成员内部类(static成员内部类和非static成员内部类),作为外部类的成员
可以有修饰符4种,final修饰,abstrct修饰,还可以在其内部定义属性即使与外部类同名也可以等
只能访问其它静态的成员,而不能访问实例成员 new Outer.Nest()
非静态内部类中不可以声明静态成员,只有将某个内部类修饰为静态类,然后才能够在这个类中定义静态的成员变量与成员方法。
局部内部类(不谈修饰符)、匿名内部类
在方法里定义局部内部类,使用较少,局部内部类只能在声明的方法内是可见的
局部内部类不能访问定义它的方法内的局部变量,除非这个变量被定义为final
常常使这个方法返回某个类或接口的对象,而这个类或接口定义在方法内部
public Comparable getComparable(){
//1.创建一个实现Comparable接口的类:局部内部类
class MyComparable implements Comparable{
@Override
public int compareTo(java.lang.Object o) {
return 0;
}
}
//2.返回一个实现类的对象
return new MyComparable();
}
匿名内部类:不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
创建一个实现Product接口的类的对象,并将此对象传入方法中
NoteBook n = new NoteBook();
t.show(n);
或创建一个实现Product接口的匿名类的对象 这个匿名类也就是匿名内部类
Product p = new Product(){
public void getName(){
System.out.println("Galaxy Note3");
}
public void getPrice(){
System.out.println("¥5288");
}
};
t.show(p);
或创建一个实现Product接口的匿名类的匿名对象
t.show(new Product(){
public void getName(){
System.out.println("Iphone5s");
}
public void getPrice(){
System.out.println("¥5288");
}
});
-------------------------------------------------------------------------------------------
设计模式:
工厂方法:
概述:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。
适用性:
1. 当一个类不知道它所必须创建的对象的类的时候
2. 当一个类希望由它的子类来指定它所创建的对象的时候
3. 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候
是设计模式中应用最为广泛的模式,在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。
FactoryMethod解决的就是这个问题,它通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。
////工厂方法
//public class TestFactory {
// public static void main(String[] args) {
// WorkFactory w = new StudentWorkFactory();
// w.getWork().doWork();
//
// }
//}
//
//interface WorkFactory {
// Work getWork();
//}
//
//class TeachWorkFactory implements WorkFactory {
//
// @Override
// public Work getWork() {
//
// return new TeachWork();// *由实现工厂的类里决定创建的对象,
// }
//
//}
//
//class StudentWorkFactory implements WorkFactory {
//
// @Override
// public Work getWork() {
//
// return new Student1Work();
// }
//
//}
//
//interface Work {
// void doWork();
//}
//
//class TeachWork implements Work {
//
// @Override
// public void doWork() {
// System.out.println("老师");
//
// }
//}
//
//class Student1Work implements Work {
//
// @Override
// public void doWork() {
// System.out.println("学生");
// }
//
//}
-----------------------------------------------------------------
代理模式:
为其他对象提供一种代理以控制对这个对象的访问。
public class TestFactory {
public static void main(String[] args) {
Object obj =new ProObject();
obj.action();
}
}
//静态代理类。 反射 动态代理
interface Object{
void action();
}
//代理类
class ProObject implements Object{
Object obj;
public ProObject(){
System.out.println("ok,创建代理类");
obj=new ObjectImp();
}
public void action(){
System.out.println("代理类执行");
obj.action();//执行代理类调用被代理类的内容
System.out.println("执行结束");
}
}
//被代理类
class ObjectImp implements Object{
@Override
public void action() {
System.out.println("被代理类的内容");
}
}