1.面向对象概述
面向对象思想是人类最自然的一种思考方式,它将所有预处理的问题抽象为对象,同时了解这些对象具有哪些相应的属性以及展示这些对象的行为,以解决这些对象面临的一些实际问题,这样就在程序开发中引入了面向对象设计的概念,面向对象的实质就是对现实世界的对象进行建模操作。
1.1 什么是对象
现实世界中,随处可见的一种事物就是对象,对象是事物存在的实体。通常会将对象划分为两部分,即动态部分与静态部分。静态部分即是“属性”,任何对象都会具备其自身属性,对象所具有的行为(动态部分)即为方法。
1.2 什么是类
类是同一类事物的统称,如果将现实世界中的一个事物抽象成对象,类就是这类对象的统称。
类是封装对象的属性和行为的载体,反过来说具有相同的属性和行为的一类实体被称为类。
在Java语言中,类中对象的行为是以方法的形式定义的,对象的属性是以成员变量的形式定义的,而类包括对象的属性和方法。
1.3 面对对象的特点
1.封装
封装是面对对象编程的核心思想,将对象的属性和行为封装起来,而将对象的属性和行为封装起来的载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。
采用封装的思想保证了类内部数据结构的完整性,应用该类的用户不能轻易的直接操纵此数据结构,而只能执行类允许公开的数据。这样避免了外部对内部数据的影响,提高了程序的可维护性。
2.继承
继承性主要利用特定对象之间的共有属性,将共有属性定义为父类,特定属性定义为子类。继承关系可以使用属性关系来表示,父类与子类存在一种层次关系。一个类处于继承体系中,它既可以是其他类的父类,为其他类提供属性和行为,也可以是其他类的子类,继承父类的属性和方法。
3.多态
(1)将父类对象应用于子类的特征就是多态,多态性允许以统一的风格编写程序,以处理种类繁多的已存在的类以及相关类。该统一风格由父类实现,根据父类统一风格的处理,就可以实例化子类的对象。
(2)由于整个时间的处理都只依赖于父类的方法,所以日后只要维护和调整父类的方法即可,这样降低了维护ud难度,又节省了时间。
(3)在提到多态的同时,不得不提到抽象类和接口,因为多态的实现并不依赖具体类,而是依赖于抽象类和接口。
(4)抽象类不能实例化对象,在多态的机制中,父类通常会被定义为抽象类,在抽象类中给出一个方法的标准,而不给出实现的具体流程。
(5)在多态的机制中,比抽象类更加方便的方式是将抽象类定义为接口。由抽象方法组成的集合就是接口。
2.类
类是封装对象的属性和行为载体,对象的属性以成员变量的形式存在,对象的方法以成员方法的形式存在。
2.1 类的构造方法(构造器)
public book(){
//构造方法体
}
public:构造方法修饰符。
book:本类的类名。
在类中定义构造方法时,需要定义无参构造器
构造方法中可以为成员变量赋值,这样实例化一个本类的对象时,相应的成员变量也将被初始化。
使用this关键字调用有参构造方法
public class Demo {
public Demo(){
this("this调用有参构造方法");
System.out.print("无参构造方法");
}
public Demo(String name){
System.out.print("有参构造函数");
}
}
在无参构造方法中可以使用this关键字调用有参的构造方法。但使用这种方式值得注意的是:
只可以在无参构造方法中的第一句使用this关键字调用有参构造方法。
2.2 类的主方法
主方法是类的入口点,他定义了程序从何处开始;主方法提供对程序流向的控制,java编译器通过主方法来执行程序。
public static void main(String[] args){
//方法体
}
主方法的特性
主方法是静态的。要直接在主方法中调用其他方法,则该方法必须也是静态的。
主方法没有返回值。
主方法的形参为数组。
用args[0]~args[n]分别代表程序的第一到第n个参数,可以使用args.length获取参数的个数。
public class Demo1 {
public static void main(String[] args) {
for(int i = 0;i<args.length;i++){
System.out.println(args[i]);
}
}
}
2.3 成员变量
在java语言中对象的属性称为成员变量,也可以称为属性。
2.4 成员方法
在java语言中使用成员方法对应于类对象的行为。
权限修饰符 返回值类型 方法名(参数类型 参数名){
...//方法体
return 返回值;
}
一个成员方法可以有参数,这个参数可以是对象也可以是基本数据类型的变量,同时成员方法有返回值和不返回值的选择,如果方法有返回值则在方法体中使用return关键字,如果没有则使用void关键字。
成员方法的返回值可以是计算结果,也可以是其他想要的数值、对象,返回值类型要与方法返回值类型相同。
在成员方法中可以调用其他成员方法和类成员变量,也可以在成员方法中定义一个变量,这个变量为局部变量。
如果一个方法中含有与成员变量同名的局部变量,则方法中对这个变量的访问以局部变量进行访问。
2.5 局部变量
局部变量是在方法被执行时创建,在方法执行结束时销毁。局部变量在使用时必须进行赋值操作或者初始化。
2.6 局部变量的有效范围
可以将局部变量的有效范围称为变量的作用域,局部变量的有效范围从该变量的声明开始到该变量的结束为止。
2.7 静态变量、常量和方法
由static修饰的变量、常量和方法被称为静态变量、常量和方法。
静态成员是属于类所有的,区别于个别对象,可以在本类或者其他类使用类名和“.”运算符调用静态成员。
类名.静态类成员
静态数据与静态方法的作用通常是为了提供共享数据方法,静态成员受public、private、protected修饰符的约束。
对静态语言的两点约束
在静态方法中不可以使用this关键字。
在静态方法中不可以直接调用非静态方法。
方法体中的局部变量不能声明为static。
小技巧
//如果在执行类时,希望先执行类的初始化动作,可以使用static定义一个静态区域。
public class example{
static{
//some
}
}
//当这段代码被执行时,首先执行static块中的程序,并且只会执行一次。
2.8 权限修饰符
public:本类、本包其他类及子类、其他包的类或者子类可见
protected:本类、同包其他类或子类可见
private:本类可见
defailt:本类、本包其他类可见
2.9 this关键字
构造器互相调用,this放在构造器首行
public Test(){
this (12);
}
public Test(int age){
this ("adf");
System.out.println("===>"+age);
}
public Test(String name){
System.out.println("asss");
}
private void setName(String name){
this.name = name;
}
this关键字被隐式的用于引用对象的成员变量和方法,this可以调用成员变量和成员方法。this引用就是对一个对象的引用。
public Book getBook(){
return this;
}
可以作为方法的返回值,将类的对象进行返回。
2.10 自定义图书类
建立Books类,定义三个成员变量分别表示书名、作者和价格,同时提供构造方法来修改成员变量。
public class Books {
private String title;
private double price;
private String autor;
public Books(String title,double price,String autor){
this.autor = autor;
this.price = price;
this.title = title;
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
public String getAutor() {
return autor;
}
}
建立测试类BookTest,创建book对象并输出其属性
public class BookTest {
public static void main(String[] args){
Books book = new Books("阿弥陀佛么么哒",49.9,"那谁谁");
System.out.println("书名:"+book.getTitle());
}
}
3.对象
对象是由类抽象出来的,所有的问题都是通过对象来处理,对象可以操作类的属性和方法解决相关的问题。
3.1 对象的创建
对象是在一类事物中抽象出某一个特例,通过这个特例来处理这类事物出现的问题,在java语言中通过new操作符来创建对象。可以在java语言中使用new操作符调用构造方法创造对象。
Test test = new Test();
Test test = new Test("a");
Test:类名
test:创建Test类对象
new:创建对象操作符
“a”:构造方法的参数
test对象被创建出来时 ,test对象就是一个对象的引用,这个引用在内存中为对象分配了存储空间,可以在构造方法中初始化成员变量,当创建对象时,自动调用构造方法,也就是说在java语言中初始化与创建是被捆绑在一起的。
每个对象都是相互独立的,在内存中占据独立的内存地址,并且每个对象都具有自己的生命周期,当一个对象的生命周期结束时,对象变成了垃圾,由java虚拟机自带的垃圾回收机制处理,不能在被使用。
3.2 访问对象的属性和行为
当用户使用new操作符创建一个对象后,可以使用“对象.类成员”来获取对象的属性和行为。对象的属性和行为在类中是通过变量和成员方法的形式来表示的,所以当对象获取类成员,也就相应的获取了对象的属性和行为。
同一个类下,对象是相互独立的
用static关键字修饰作为全局变量使用
3.3 对象的引用
类名 对象引用名称
引用只是存放一个对象的内存地址,并非存放一个对象,严格说引用和对象是不同的,但是可以将这种区别忽略,如可以简单的说book是Book类的一个对象,而事实上应该说book是包含Book对象的一个引用。
3.4 对象的比较
两种对象的比较方法,分别为“==”运算符与equals()方法。
equals()方法是String类中的方法,它用于比较两个对象所指的内容是否相同。
“==”运算符比较的是两个对象的引用地址是否相等。
3.5 对象的销毁
每个对象都有生命周期,当对象的生命周期结束时,分配给该对象的内存地址将会被收回,在其他语言中需要手动收回废弃的对象,java语言拥有一套完整的垃圾回收机制,用户不必担心废弃的对象占用内存,垃圾回收器将回收无用的占用内存的资源。
何种对象会被java虚拟机视为垃圾
(1)对象引用超出其作用范围
(2)将对象赋值为null
垃圾回收器只能回收那些由new操作符创建的对象,如果某些对象不是通过new操作符在内存中获取的一块内存区域,这种对象可能不被垃圾回收机制所识别,所以在java语言中提供了一个finalize()方法,这个方法是Object类的方法,他被声明为protected,用户可以在自己的类中定义这个方法,如果用户在类中定义了finalize()方法,在垃圾回收时首先调用该方法,并且在下一次垃圾动作发生时,才能真正的回收对象占用的内存。
垃圾回收或是finalize()方法不保证一定发生,如java虚拟机面临内存耗损殆尽的情形,他是不会执行垃圾回收的。
垃圾回收不受人为控制,具体时间也是不确定的,所以finalize()方法也就无法执行,为此,java提供了System.gc()方法强制启动垃圾回收器。
3.6 统计图书销量
(1)在项目中创建Book类,在类中定义了一个静态的成员变量用于保存实例化的次数,在构造方法中实现技术器功能。
public class Book {
private static int counter = 0;
public Book(String title){
System.out.println("售出此书:"+title);
counter++;
}
public static int getCounter(){
return counter;
}
}
(2)创建Book类对象并输出创建对象的个数。
import java.util.Random;
public class Test1 {
public static void main(String[] args) {
String[] titles = {"a","b","c"};
for(int i = 0;i<5;i++){
new Book(titles[new Random().nextInt(3)]);
}
System.out.println("总计销售了"+Book.getCounter());
}
}
3.7 计算对象哈希码
java语言中创建的对象是保存在堆中的,为了提高查找的速度而使用散列查找。散列查找的基本思想是定义一个键来映射对象所在的内存地址。当需要查找对象时,直接查找键就可以,这样就不用遍历整个堆来查找对象。
(1)在项目中创建Cat类,在类中定义4个成员变量分别表示猫咪的名字、年龄、重量、颜色,并提供构造方法来设置这些属性值。本类的重点内容在于重写equals()方法和hashCode()方法可以让相同的对象保存在相同的位置。
import java.awt.Color;
public class Cats {
private String name;
private int age;
private double weight;
private Color color;
public Cats(String name,int age,double weight,Color color) {
this.name = name;
this.age = age;
this.weight = weight;
this.color = color;
}
@Override
public boolean equals(Object obj){//重写Object类中的equals方法
if(this == obj){
return true;
}
if(obj == null){
return false;
}
if(getClass()!=obj.getClass()){
return false;
}
Cats cat =(Cats)obj;
return name.equals(cat.name)&&(age==cat.age)&&
(color==cat.color)&&(weight==cat.weight);
}
@Override
public int hashCode(){ //重写Object类中的hashCode方法
return 7*name.hashCode()+11*new Integer(age).hashCode()
+13*new Double(weight).hashCode()+17*color.hashCode();
}
}
(2)在main方法中建立三只猫咪,并为其初始化,然后输出猫咪的哈希码和比较结果。
import java.awt.Color;
public class Test2 {
public static void main(String[] args) {
Cats cat1 = new Cats("白白",12,21,Color.white);
Cats cat2 = new Cats("兰兰",12,21,Color.blue);
Cats cat3 = new Cats("白白",12,21,Color.WHITE);
System.out.println("猫咪1号的哈希码:"+cat1.hashCode());
System.out.println("猫咪2号的哈希码:"+cat2.hashCode());
System.out.println("猫咪3号的哈希码:"+cat3.hashCode());
System.out.println("猫咪1号是否与2号相同:"+cat1.equals(cat2));
System.out.println("猫咪1号是否与3号相同:"+cat1.equals(cat3));
}
}
4.经典范例
4.1 汉诺塔问题求解
汉诺塔问题描述如下:有A、B和C 3根柱子,在A上从下往上按照从大到小的顺序放着64个圆盘,以B为中介,把盘子全部移动到C上。移动过程中,要求任意盘子的下面要么没有盘子,要么只能有比他大的盘子。
public class HanoiTower {
public static void moveDish(int level,char from,char inter,char to){
if(level==1){
System.out.println("从"+from+"移动盘子1号到"+to);
}else{
moveDish(level-1, from, to, inter);
System.out.println("从"+from+"移动盘子"+level+"号到"+to);
moveDish(level-1, inter, from, to);
}
}
public static void main(String[] args) {
int nDisks = 4;
moveDish(nDisks,'A','B','C');
}
}
4.2 单例模式的作用
(1)在项目中创建Emperor类,在类中将构造方法设置为私有,并提供了一个静态方法用于获得该类的实例。
public class Emperor {
private static Emperor emperor = null;
private Emperor() { //减少错误是使用,避免程序混乱
}
public static Emperor getInstance(){
if(emperor == null){
emperor = new Emperor();
}
return emperor;
}
public void getName(){
System.out.println("我是皇帝:Yking");
}
}
(2)在类中创建三个皇帝对象,并进行输出。
public class Test {
public static void main(String[] args) {
System.out.println("创建皇帝对象1:");
Emperor emperor1 = Emperor.getInstance();
emperor1.getName();
System.out.println("创建皇帝对象2:");
Emperor emperor2 = Emperor.getInstance();
emperor2.getName();
System.out.println("创建皇帝对象3:");
Emperor emperor3 = Emperor.getInstance();
emperor3.getName();
}
}