java面向对象OOP
面向过程:数据和方法是分离的。
面向对象:数据和方法集成到一起,便于扩展
面向对象和面向过程
- 都是解决问题的思维方式,都是代码组织的方式。
- 解决简单问题可以使用面向过程
- 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程。
面向对象思考方式:
遇到复杂问题,
先从问题中找名词,然后确立这些名词哪些可以作为类,再根据问题需求确定的类的属 性和方法,确定类之间的关系。
面向对象的三大特征
- 隐藏/封装(encapsulation, capsul胶囊) 需要让用户知道的暴露出来,不需要让用户了解的全部隐藏起来。这就是封装。
- 继承(extend) 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。 继承的另一个好处是:代码重用,让我们不再重新发明轮子(don’t reinvent wheels)
- 多态(polymorphism) 为了处理问题的灵活性。 就是在做之前,不要把话说死,说的模糊一点想象空间大一点,真做的时候 再具体化。
对象和类
-
**对象:是具体的实物 **
-
**类:是对对象的抽象 **
类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。
类和对象的关系
对象和类的关系是特殊到一般,具体到抽象。
- 类:我们叫做 class 。
- 对象:我们叫做 Object , instance (实例)。以后我们说某个类的对象,某个类的实例。是一样的意 思。
类的定义
语法:
class 类名{
}
class是定义类的关键字
类名需要满足命名规则和规范,类的命名应该使用大驼峰命名原则
类的属性
属性用于定义该类或该类对象包含的数据或者说静态特征。
属性作用范围是整个类体
属性定义格式:在类中,所有方法和代码块之外
[修饰符] 属性类型 属性名 [= 默认值] ;
在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化。(数值:0,0.0 char:\u0000, boolean:false, 所有引用类型:null)
类的方法
方法则用于定义该类或该类的实例的行为特征和功能实现。
方法是类和对象行为特征的抽象。
方法很类 似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成;面向对象 中,整个程序的基本单位是类,方法是从属于对象
[修饰符] 方法返回值类型 方法名(形参列表) {
方法体;
}
对象的创建和使用
必须使用 new
关键字创建对象。
Person person= new Person ();
使用对象(引用) . 成员变量来引用对象的成员变量。 person.age;
使用**对象(引用) . 方法名(参数列表)**来调用对象的方法。 person. setAge(23);
同一类的每个对象有不同的成员变量存储空间。
同一类的每个对象共享该类的方法。
构造器(constructor)
构造器用于构造该类的实例。
Java通过new关键字来调用构造器,从而返回该类的实例。 构造器是一种特殊的方法: 格式如下
[修饰符] 类名(形参列表){
//n条语句
}
注意:
- 通过new关键字调用!!
- 构造器虽然有返回值,但是不能定义返回类型(返回值的类型肯定是本类),不能在构造器里使用带 数据的return语句。
- 没有定义构造器,则编译代码时(java–>class)会自动定义一个无参的构造函数。如果 已定义则编译器不会添加!
- 构造器的方法名必须和类名一致!
内存分析
栈:
- 每个线程私有,不能实现线程间的共享!
- 局部变量放置于栈中。
- 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆:
- 放置new出来的对象!
- 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区:
- 被所有线程共享!
- 用来存放程序中永远是不变或唯一的内容。(类代码信息、静态变量、字符串常量)
方法的重载(overloading)
同一个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载 (method overloading)。
-
参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数 列表不同。
-
重载的结果,可以让一个程序段尽量减少代码和方法的种类。
-
方法的重载的规则:
- 方法名称必须相同。
- 参数列表必须不同。
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以称为方法的重载
- 类 方法名 ,参数的顺序 类型 数量 ,返回值不同并不能构成重载
-
注意:
- 类型就近原则–》将来重载方法的时候,要特别主要参数类型的自动转换 如果不慎重就有可能调用不到指定的方法
- 可变参数的方法–》写法 类型… 标识符 必须位于参数列表的最后,一个方法只能有一个可变参数列表 可变参数列表传参的数量可以是 0-任意多个,都以数组的方式进行存放
this关键字
基本含义:当前对象
普通方法中,this总是指向调用该方法的对象。
构造方法中,this总是指向正要初始化的对象。
this最常的用法:
- 让类中的一个方法,访问该类的另一个方法(this.方法名())或属性(this.属性名)。访问属性更常 用,用来区分同名的形参和属性。
- 使用this关键字调用重载构造方法。避免写相同的初始化代码,只能在构造方法中用,并且必须位 于构造方法的第一句。
this不能用于static方法。
static 关键字
static属性: 属性被所有对象共享;
这个属性先于对象存在的 static 方法:这个方法先于对象存在
在类中,用static声明的成员变量为静态成员变量
用static声明的方法为静态方法
- 不需要对象,就可以调用( 类名.方法名() )
- 在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员。也不 可以出现this关键字
JDK中的主要包
- java.lang -包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供 常用功能。
- java.net -包含执行与网络相关的操作的类。
- java.io -包含能提供多种输入/输出功能的类。
- java.util -包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。
1.封装(encapsulation)
封装的作用和含义
专业角度解释:我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外 部干涉;低耦合:仅暴露少量的方法给外部使用。
编程中封装的具体意义:
- 便于调用者调用。
- 良好的封装,便于修改内部代码,提高可维护性。
- 良好的封装,可进行数据完整性检测,保证数据的有效性。
访问控制符
访问控制符、修饰符总表
外部类/接口 | 成员属性 | 方法 | 构造器 | 初始化块 | 成员内部类 | 局部成员 | |
---|---|---|---|---|---|---|---|
访问控制符 | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
public | √ | √ | √ | √ | √ | ||
protected | √ | √ | √ | √ | |||
包访问控制符(默认) | √ | √ | √ | √ | √ | ||
private | √ | √ | √ | √ | |||
修饰符 | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
abstract | √ | √ | √ | ||||
final | √ | √ | √ | √ | √ | ||
static | √ | √ | √ | √ | |||
strictfp | √ | √ | √ | ||||
synchronized | √ | ||||||
native | √ | ||||||
transient | √ | ||||||
volatile | √ | ||||||
default | √ |
封装的使用细节
类的属性的处理: 一般使用 private 。
(除非本属性确定会让子类继承)
提供相应的 get/set 方法来访问相关属性. 这些方法通常是 public ,从而提供对属性的读取操作。 (注意:boolean变量的get方法是用:is开头!)
一些只用于本类的辅助性方法可以用 private ,希望其他类调用的方法用 public 。
定义一类, Person, 姓名, 年龄, 性别(boolean)
static:在有权限的基础上, 规定了调用的方式,根据需求 权限:决定你是否能够有机会访问,根据需求(属性private, public)
2.继承(extends, inheritance)
继承的作用:
第一好处:继承的本质在于抽象。类是对对象的抽象,继承是对某一批类的抽象。
第二好处:为了提高代码的复用性。(事实上,利用组合可以更好的实现代码复用!)
extands 的意思是“扩展”。子类是父类的扩展。
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
如何使用
使用 extends 关键字即可。
使用要点
子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法)。
java中只有单继承,没有像c++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维 护。
多继承,就是为了实现代码 的复用性,却引入了复杂性,使得系统类之间的关系混乱。
java中的多继承,可以通过接口来实现
如果定义一个类时,没有调用extends,则它的父类是: java.lang.Object 。 不同的叫法:超类、父类、基类、子类、派生类
方法的重写(override)
有继承关系的两个类, 父类中的方法不能满足子类需求的时候需要子类自己将该方法重新实现以下
方法名, 参数(个数、顺序、类型),返回值,权限
- 在子类中可以根据需要对从基类中继承来的方法进行重写。
- 重写方法必须和被重写方法具有相同方法名称、参数列表和返回类型。
- 重写方法不能使用比被重写方法更严格的访问权限。(由于多态)
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
animal.shout();
Dog dog = new Dog();
dog.shout();
}
}
class Animal{
void shout(){
System.out.println("发出声音!");
}
}
class Dog extends Animal {
void shout(){
System.out.println("旺旺旺!");
}
}
父类方法的重写:
- “**”:方法名、形参列表相同。
- “≤≤”:返回值类型和异常类型,子类小于等于父类。
- “≥”:访问权限,子类大于等于父类
以下方法不能重写:
- private 修饰的方法
- static 修饰的方法
- final 修饰的方法
private成员问题:子类继承父类的一切东西. 中说到, 子类对象拥有父类对象的完整拷贝.
实例化一个类是从最顶级的超类 开始实例化的, 是一层一层的包裹结构.
private限制访问方式只能在类的内部, 这仅仅是一个访问控制, 实际上子类对象拥有父类对象的一切
构造方法调用顺序:
- 根据super的说明,构造方法第一句总是:super(…)来调用父类对应的构造方法。
- 先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。
super关键字
super 是直接父类对象的引用。可以通过 super 来访问父类中被子类覆盖的方法或属性。
创建子类对象的时候一定会先创建父类对象(这是一个递归的过程),直到Object
- 访问父类的属性: super.属性名; 前提是有访问权限
- 方法父类的方法: super.方法名(); 前提是有访问权限
构造函数中: 任何类的构造函数中,若是构造函数的第一行代码没有显式的调用super(…);那么Java默 认都会调用super();作为父类的初始化函数。
public class Test {
public static void main(String[] args) {
new ChildClass().f();
}
}
class FatherClass {
public int value;
public void f(){
value = 100;
System.out.println
("FatherClass.value="+value);
}
}
class ChildClass extends FatherClass {
public int value;
public void f() {
super.f();
value = 200;
System.out.println
("ChildClass.value="+value);
System.out.println(value);
System.out.println(super.value);
}
}
多态(polymorphism)
多态是方法的多态,属性没有多态性。
多态的存在要有3个必要条件:要有继承,要有方法重写,父类引用指向子类对象
public class TestPolym {
public static void main(String[] args) {
Animal animal = new Dog(); //向上可以自动转型
System.out.println(animal.age); //属性调用时,仍然是基类的属性。属性没有多态!
// animal.shout();
animalCry(new Dog());
//传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。
//如果没有多态,我们这里需要写很多重载的方法。如果增加一种动物,就需要重载一种动物的喊
叫方法。非常麻烦。
//有了多态,只需要增加这个类继承Animal基类就可以了。
animalCry(new Cat());
Dog dog = (Dog) animal; //编写程序时,如果想调用运行时类型的方法,只能进行类型转
换。不然通不过编译器的检查。
dog.gnawBone();
System.out.println(dog instanceof Animal);
System.out.println(animal instanceof Cat);
System.out.println(animal instanceof Dog);
}
static void animalCry(Animal a){
a.shout();
}
}
class Animal {
int age=10;
public void shout(){
System.out.println("叫了一声!");
}
}
class Dog extends Animal {
int age=28;
public void shout() {
System.out.println("旺旺旺!");
}
public void gnawBone(){
System.out.println("我再啃骨头");
}
}
class Cat extends Animal {
int age=18;
public void shout() {
System.out.println("喵喵喵喵!");
}
}