Java面向对象笔记(详细)

本文详细介绍了面向对象编程的核心概念,包括封装、继承和多态。封装确保了数据的安全性和代码的复用性,通过构造函数和this关键字管理对象。继承允许代码重用和扩展,子类可以继承父类的属性和方法,同时通过super关键字调用父类构造器。多态性使得不同的对象可以响应相同的接口,增强了代码的灵活性。此外,还讨论了接口、抽象类以及匿名内部类在实现这些概念中的作用。
摘要由CSDN通过智能技术生成

面向过程程序设计以功能分解为基础设计软件系统,它的缺点:代码中数据结构和和功能可重用性差;软件可维护性差;它适合小程序,单个算法或很成熟的系统;而不适合大型系统或需要频繁变化的系统。

面向对象程序设计步骤:
1.确定系统中接收消息的对象;
2.确定每个对象上执行消息的操作;

类和类之间的关系:
1.相互独立
2.通过继承建立联系
3.通过type(interface)建立联系,通过接口(interface)实现多继承

封装面向对象程式设计中,封装(Encapsulation)就是把类的内部与外界隔离起来,把实现数据和方法的包装(隐藏),防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过特定的方式。
封装的优点:
1.提高安全性
2.提高代码的复用性
3.隐藏了实现细节

实现类时,先设计接口,再考虑接口的具体实现。

一个例子:


public class Employee {    
	int age;    
	String name;    
	public Employee(String name){//构造函数        
		this.name = name;    
	}    
	public void setAge(int a){        
		age = a;    
	}    
	public void printEmp(){        
		System.out.println("Age:" + age);        
		System.out.println("Name:" + name);    
	}    
	public static void main(String[] args){        
		Employee manA = new Employee("Cobb");//创建对象并初始化       
		manA.setAge(28);        
		manA.printEmp();    
	}
}

this是成员函数中一个特殊的固有的本地变量,它表达了调用这个函数的那个变量,this管理这这个对象中的成员变量。在成员函数内部调用自己的其他函数不需要用.运算符。成员变量的生存期是对象的生存期,作用域是类内部的成员函数。Java中我们把不用担心对象生存期的问题,因为有Java的垃圾回收(GC)机制。声明对象却没有new会被自动初始化为null。

构造函数

this关键字可以在构造函数中引用同一个类下其他的构造函数。例:this(参数列表);如果一个类有不同的初始化形式,就需要写多个构造方法,灵活地调用构造方法需要善用this关键字。子类构造函数中没有super()时系统会为所有的构造函数自动隐式地调用一个super(),这时若父类中没有默认构造函数,编译就会出问题。
一些规则:
1.构造函数的第一行代码可以是一下两种之一:
super(参数列表);//用来初始化父类成员
this(参数列表);//用来调用同类构造函数来实现多种形式的初始化
2.在一个构造函数中不能同时存在super()和this()
3.当定义一个构造函数时,同时也应该定义一个默认构造函数(无参构造函数);

创建对象:

声明:声明一个对象,包括对象名称和对象类型。实例化:使用关键字 new 来创建一个对象。初始化:使用 new 创建对象时,会调用构造方法初始化对象。
可以定义多个构造函数(重载);

成员变量定义初始化:

在成员变量定义的地方可以直接给出初始值;没有初始化的成员变量会自动设为0值;对象变量的0值表示没有管理任何对象,也可以给null;定义初始化可以调用函数。

对象的组合与交互

当一个对象里有多个对象的时候,那么问题来了,那些对象之间是如何交互的,对象和对象之间的联系是如何建立的,对象如何和其他对象交流。对象和对象之间的联系紧密程度叫做耦合。对象和对象的耦合程度越紧,表现在源代码上,就是它们的代码是互相依赖、互相牵制的。我们理想的模型,是对象和对象之间的耦合要尽可能的松,平行的对象要尽量减少直接联系,让更高层次的对象来提供通信服务。所以我们希望,部件对象是相互独立互不影响的,它们之间的交互由顶层的对象来完成。

时钟的例子:
部件类Display用来表示数字:


package Clock;
public class Display {       
	private int num;       
	private int limit;       
	public Display(int limit){             
		this.limit = limit;       
	}       
	public void increase() {             
		num ++;             
		if(num == limit) {                    
			num = 0;             
		}       
	}       
	public int getNum() {             
		return num;       
	}       
	public static void main(String[] args) {             
		// TODO Auto-generated method stub                    
	}
}

Clock类由两个部件Display类组成,分别命名为hour,min;当min满60之后hour要加一,如何实现hour和min之间的这个交互呢?Clock类的代码:


package Clock;
public class Clock {       
	private Display hour = new Display(24);       
	private Display min = new Display(60);       
	public void start() {//hour,min这两个部件对象是相互独立的,它们之间的交互由start函数来完成             
		min.increase();             
		if(min.getNum() == 0) {                    
			hour.increase();             
		}             
		System.out.printf("%02d : %02d\n",hour.getNum(),min.getNum());       
	}       
	public static void main(String[] args) {             
		// TODO Auto-generated method stub             
		Clock c = new Clock();             
		int i;             
		for(i=0;i<1000;i++) {                    
		c.start();             
		}       
	}
}

Clock类中定义的start函数来实现hour和min这两个对象的交互。
private变量只有在这个类内部才能访问,private是针对类而言的,而不是对象。所以同一类中的不同对象可以互相访问私有成员。
注:类内部指的是成员函数和定义初始化。public则是任何人在任何地方都能使用。
如果成员函数前既没有private也没有public,则为default(包访问性),意思是只有在同一个包(package)中可以使用/访问。

类变量和类函数(静态成员)

在类中定义带static的变量称为类变量。如:private static int a = 1;类变量是属于类的,而不属于变量。我们可以通过类或该类的变量来访问类变量。如:Display.a;
同理,带static的函数称为类函数,如我们的main函数。类函数是属于类的,不属于对象。static函数可以调用static函数,不能调用没有static的函数。静态成员属于类,用类名来调用。

继承

继承格式:
class 子类 extends 父类 {
……
}

子类就是父类的特殊化,父类是子类的泛化(抽象)。子类继承了父类的所有东西(除了构造函数),父类有的子类都有,父类能用的子类可能都能用(涉及访问权限的问题)。子类的对象可以被看作是父类的对象。子类可以在父类的基础上扩展新的东西。Java的继承只允许单继承,即一个类只能有一个父类。protected属性:只有包内其它类、自己和子类可以访问。

父类和子类的关系
在构造一个子类的对象时,父类的构造方法也是会被调用的,而且父类的构造方法在子类的构造方法之前被调用。在程序运行过程中,子类对象的一部分空间存放的是父类对象。因为子类从父类得到继承,在子类对象初始化过程中可能会使用到父类的成员。所以父类的空间正是要先被初始化的,然后子类的空间才得到初始化。在这个过程中,如果父类的构造方法需要参数,如何传递参数就很重要了。

子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super() 关键字调用父类的构造器并配以适当的参数列表。如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

父类的private子类不能直接访问,只能通过父类的函数去操作,因为那是private的。需要改成protected子类才能访问。如果子类中有和父类中同名的private的变量,那么父类的那个变量在子类中就被隐藏(hiding)了。子类中与父类相同的类函数会被隐藏。如果父类和子类都有一个叫print()的成员函数且参数表完全相同,在子类中父类的print()就被子类的覆盖(override)了,如果想在子类中使用父类的print(),就要这么做:super.print();

@override告诉编译器在@override下面写的那个函数是子类覆盖父类的函数(它们的参数表、修饰符要完全相同),编译器会帮你做检查

数据成员编译时是静态绑定,也就是变量的类型取决于它的修饰符类型,这意味着可以用强制类型转换(将子类强转成父类)可以将隐藏的变量挖出来,但是覆盖却不能了。例:sub.name;中name的值就取决于.name前面的类型名sub。关于覆盖的函数:当函数返回的是引用类型时,父类方法中的返回类型只要兼容子类的返回类型就行;子类方法的访问限制性要同于或低于父类方法。当父类和子类中有相同的static函数时,它们之间的关系是隐藏。此时就不是动态绑定了,而是静态。

而非static成员函数是动态绑定的,调用哪个函数取决于运行时引用变量指向的那个真实对象的类型。(多态!!!)

多态性( polymorphism)

子类可以被当作父类,反之则不行。子类对象可以放进存放父类对象的容器里。
多态性的三个条件:
1.继承
2.方法签名一致并且在父类和子类中同时实现
3.让基类类型的引用变量指向子类对象(使得编译时可以通过,运行时调用的却是子类的方法)
多态变量:
1.Java的对象变量是多态的,它能保存不止一种类型的变量;
2.它可以保存声明类型的对象及其子类对象;
3.当把子类对象赋给父类变量时,就发生了向上造型;

函数调用的绑定:
当调用一个方法时,究竟应该调用哪个方法,这件事情叫做绑定。
静态绑定:根据变量的声明类型来决定;
动态绑定:根据变量的动态类型来决定;
Java中默认所有的绑定都是动态绑定。

多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。比如在调用一个函数(同一个接口)时,到底调用哪个类中的函数(不同操作)是由动态类型(不同的实例)决定的。
or:多态性指同一界面形式不同对象类中的不同实现的能力(利用一个单一接口隐藏大量不同的实现过程。客户端可以调用操作而不用管对象类型。

所有Java中的类都继承(直接或间接)自Object类。

抽象类

抽象类可以包含和一般类能拥有的所有东西,但它不能被初始化和实例化,所以不能由抽象类产生对象。但是抽象类可以作为父类使用,必须通过继承来使用它。抽象类为子类提供基本信息。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
如果一个类有了一个抽象的方法,这个类就必须声明为抽象类。
如果父类是抽象类,那么子类必须覆盖所有在父类中的抽象方法,否则子类也成为一个抽象类。一个抽象类可以没有任何抽象方法,所有的方法都有方法体,但是整个类是抽象的。设计这样的抽象类主要是为了防止制造它的对象出来。

创建抽象类:
public abstract class Shape{}

设计原则

封装,可扩展性,框架加数据
数据与表现分离:(前后端分离)程序业务逻辑与表现无关,表现可以是图形/文本,本地/远程将程序要实现的功能分配到合适的类/对象中。

接口(interface)

Java把抽象的概念又更推进了一步。这就是接口(Interface)。接口其实就是完全抽象的类,因此接口和类的地位是一样的,此前所有针对类的所有法则同样适用于接口。接口所有的方法都是没有方法体的,而且都是public abstract,即使你没有这样声明。而接口中的所有成员变量都是public static final的变量,并且必须有定义初始化,因为所有变量都必须在编译的时候有确定值。
接口和类的地位是一样的。因此我们可以看到其定义方式跟类也是基本上一样的。当然,其中的所有方法都没有方法体。而当类从接口得到继承的时候,Java用另外一个动词:实现(implements)。当然,实现接口的类必须覆盖接口中所有的方法,否则这个类就成为一个抽象类。
Java不允许多继承,但是允许一个类实现多个接口,也允许一个接口从多个接口得到继承,但是不允许接口从类继承。接口没有构造方法。接口中所有的方法必须是抽象方法。

接口的声明:

public interface NameOfInterface{   
  //任何类型 final, static 字段  
  //抽象方法
}
interface Animal {   
     public void eat();   
     public void travel();
}

接口有以下特性:
接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。接口中的方法都是公有的。

接口的实现:

public class MammalInt implements Animal{   
  public void eat(){
  //覆盖      
  System.out.println("Mammal eats");   
  }   
  public void travel(){//覆盖      
  System.out.println("Mammal travels");   
  }    
}

接口的继承:

一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
多继承:

public interface Hockey extends Sports, Event{}

标记接口:

没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
1.建立一个公共的父接口;
2.向一个类添加数据类型:实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法)。
实现了这个接口的类,它的对象都可以绑定在这个接口上,用接口名声明变量指向这个类的对象。

匿名内部类

匿名内部类是没有名字的写在其他类内部的类。作用:简化代码把子类继承父类/实现接口、重写父类/接口方法、创建子类/实现类对象合成一步完成;匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:

new 父类/接口(){    
	重写父类/接口中的方法
}

对象拷贝

浅拷贝例:假设想要克隆Student类,则该类要实现Cloneable接口,重写clone方法。

class Student implements Cloneable{      
	private int number;        
	public int getNumber() {          
		return number;      
	}        
	public void setNumber(int number) {          
		this.number = number;      
	}            
	@Override      
	public Object clone() {          
		Student stu = null;          
		try{              
			stu = (Student)super.clone();          
		}catch(CloneNotSupportedException e) {              
			e.printStackTrace();          
		}          
		return stu;      
	}  
}  

深度拷贝:

若该类中的成员方法有其他类对象,想要拷贝该类对象,则这个类也要实现Cloneable接口,重写clone方法。
更好的方法是使用序列化与反序列化来实现深度拷贝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值