面向对象的程序设计思想

  • 类 :在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽 象出来,用类来表示。
  • 典型的语言:Java、C#、C++、Python、Ruby和PHP等 代码结构:以 类 为组织单位。
  • 每种事物都具备自己的 属性 和 行为/功能 。
  • 是一种“设计者思维 ,适合解决复杂问题。代码扩展性强、可维护性高。

面向对象特征

封装、继承、多态、(抽象)

面向对象例子

把猪肉放进冰箱

面向过程的做法

1.打开冰箱
2.把猪肉装进冰箱
3.把冰箱门关住

面向对象的做法

人{
	打开(冰箱){
		冰箱.开门();
	}
	操作(猪肉){
		猪肉.进入冰箱();
	}
	关闭(冰箱){
		冰箱.关门();
	}
}
冰箱{
	开门();
	关门();
}
猪肉{
	进入冰箱();
}

类和对象

类(Class) 和 对象(Object) 是面向对象的核心概念

什么是类

类:具有相同特征的事物的抽象描述,是 抽象的 、概念上的定义

什么是对象

对象:实际存在的该类事物的 每个个体 ,是 具体的 ,因而也称为 实例(instance)

简单理解:类的一个抽象概念的一个人,对象是实实在在的一个人

类的定义

使用关键字 class

class [类名]{
  属性声明;
  方法声明;
}

例如创建一个猫类

class cat{
  // 属性声明
  String name;
  int age;
  // 方法声明
  public void eat(){ System.out.println("猫吃东西~~"); }
}

对象的创建

使用关键字 new

对象的创建也称为类的实例化

使用对象名.属性 对象名.方法的方式访问对象成员(包括属性和方法)

//方式1:给创建的对象命名
//把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
类名 对象名 = new 类名();
//方式2:
new 类名()//也称为匿名对象

例如实例化一个猫类 / 创建一个猫对象

class CatTest{
	public static void main(String[] args){
		//创建Cat类的对象
    Cat cat1 = new Cat();
		// 访问对象属性和方法
    cat1.age = 12;
    System.out.printIn("cat age is" + cat1.age);
    cat1.eat();
    
    // 创建第二个Cat类对象
     Cat cat2 = new Cat();
		// 访问对象属性和方法
    cat2.age = 9;
    System.out.printIn("cat age is" + cat2.age);	
    cat2.eat();
	}
}

属性如果不赋值,有默认值,规则和数组一致

具体: int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char 0, boolean false,String null

对象内存解析

jvm 内存解析图

  • 堆:凡是new出来的结构(对象、数组)都放在堆空间中
  • 对象的属性存放在堆空间中
  • 创建一个类的多个对象(比如cat1,cat2),则每个对象都拥有当前类的一套"副本"(即属性)
  • 当通过一个对象修改其属性时,不会影响其它对象此属性的值。
  • 当声明一个新的变量使用现有的对象进行赋值时(比如cat3 = cat1),此时并没有在堆空间中创 建新的对象,而是两个变量共同指向了堆空间中同一个对象
  • 当通过一个对象修改属性时, 会影响另外一个对象对此属性的调用

成员变量

语法格式

[修饰符1] class 类名{
	[修饰符2] 数据类型 成员变量名 [= 初始化值];
}

  • 位置要求:必须在类中,方法外
  • 修饰符常用的权限修饰符有:private、缺省、protected、public 其他修饰符:static、final
  • 数据类型 任何基本数据类型(如int、Boolean) 或 任何引用数据类型
  • 成员变量名 属于标识符,符合命名规则和规范即可
  • 初始化值 根据情况,可以显式赋值;也可以不赋值,使用默认值

例如

public class Person{
  private int age; //声明private变量 age
  public String name = “Lila”; //声明public变量 name
}

局部变量

在方法体内部等位置声明的变量称为局部变量,局部变量作用域只在方法体内部 / 代码块

public class Person{
   int age; // 成员变量
  
  public void sayHello(){
    String msg = "hello"; // 局部变量
    System.out.printIn(msg);
  }

}

成员变量和局部变量的不同

  • 作用域 (1)实例变量:通过对象就可以使用,本类中直接调用,其他类中 “对象.实例变量”(2)局 部变量:出了作用域就不能使用

  • 在内存中存储的位置不同 (1)实例变量:堆 (2)局部变量:栈

  • 修饰符 (1)实例变量:public,protected,private,final,volatile,transient等 (2)局部变 量:final

  • 默认值 (1)实例变量:有默认值 (2)局部变量:没有,必须手动初始化。其中的形参比较特殊, 靠实参给它初始化。

this 关键字

哪个对象调用,this就代表哪个对象

当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加 this 来表明该变量是类的成员变量

我们可以用 this 来区分 成员变量和局部变量

比如

class Cat{
  String name;
  public void setName(String name){
    // this.name 表示成员变量 / 属性
    // 后面的 name 表示传进来的形参 即局部变量
    this.name =  name;
  }
}

this 可以作为一个类中构造器相互调用的特殊格式

比如

public class Student {
   private String name;
   private int age;
   // 无参构造
   public Student() {
  		// this("",18);//调用本类有参构造器
   }
   // 有参构造
   public Student(String name) {
   	this();//调用本类无参构造器
   	this.name = name;
   }
   // 有参构造
   public Student(String name,int age){
   	this(name);//调用本类中有一个 String 参数的构造器
   	this.age = age;
   }
 }

方法

方法的概念

方法 是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为 函数 或 过程 。

将功能封装为方法的目的是,可 实现代码重用,减少冗余,简化代码 Java里的方法 不能独立存在

所有的方法必须定义在类里

语法格式

[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{
		方法体的功能代码
}

例如

public class Person {
  public static void main(String[] args) {
    Person p1 = new Person();
    p1.eat();
  }
  public static void eat() {
    sleep();
    System.out.println("人:吃饭");
  }
  public static void sleep(){
    System.out.println("人:睡觉");
    doSport();
  }
  public static void doSport(){
    System.out.println("人:运动");
  }
}

内存分析

方法重载

  • 方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可
  • 重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参 数类型)。调用时,根据方法参数列表的不同来区别。
  • 重载方法调用:JVM通过方法的参数列表,调用匹配的方法。 先找个数、类型最匹配的 再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错

举例说明

//返回两个整数的和
public int add(int x,int y){
  return x+y;
}
//返回三个整数的和
public int add(int x,int y,int z){
	return x+y+z;
}
//返回两个小数的和
public double add(double x,double y){
	return x+y;
}

注意:方法的重载和返回值类型无关

可变个数的形参

当定义一个方法时,形参的类型可以 确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参

格式:

方法名(参数的类型名 ...参数名)

例如:

//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static String test(char seperator ,String...books){
  String str = "";
  for (int i = 0; i < books.length; i++) {
  	if(i==0){
  		str += books[i];
  	}else{
  		str += seperator + books[i];
  	}
  }
  return str;
}

String... 表示接受的是可变参数,类型是String,即可以接收多个String

使用可变参数时候,可以当数组来使用,books可以当做数组

形参和实参

  • 形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参

  • 实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称 为实际参数,简称实参

行参的值传递根据数据类型来传递的

基本数据类型:将实参基本数据类型变量的数据值传递给形参

引用数据类型:将实参引用数据类型变量的地址值传递给形参

package

package,称为包,用于指明该文件中定义的类、接口等结构所在的包

package pack1.pack2; //指定类 PackageTest 属于包 pack1.pack2
public class PackageTest{
	public void display(){
		System.out.println("in method display()");
	}
}

说明:

  • 一个源文件只能有一个声明包的 package 语句

  • 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意,取包名时不能使用java.xx

  • 包对应于文件系统的目录,package 语句中用 . 来指明包(目录)的层次,每.一次就 表示一层文件目录

  • 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)

  • 不同的包下可以定义同名的结构(类、接口)

import

为了使用定义在其它包中的 Java 类,需用 import 语句来显式引入指定包下所 需要的类

语法格式:import 包名.类名

说明:

  • 如果使用 a.*导入结构,表示可以导入 a 包下的所有的结构
  • 如果导入的类或接口是java.lang 包下,或者是当前包下的,则可以省略 import 语句
  • 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用 的是哪个类

封装性

把客观事物封装成抽象概念的类,并且类可以把自己的数据和 方法只向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息

通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想

权限修饰符

实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制

修饰符 本类内部 本包内 其他包的子类 其他包非子类
private
protected
public

具体修饰的结构:

  • 外部类:public、缺省
  • 成员变量、成员方法、构造器、成员内部类:public、protected、缺省、 private

私有化

成员变量私有化

私有化类的成员变量,提供公共的 get 和 set 方法,对外暴露获取和修 改属性的功能

例如

public class Book {
  	// 1.使用 private 修饰成员变量 
    private String bookName;
    private double price;

  
   // 2.提供 getXxx 方法 / setXxx 方法,可以访问成员变量,
    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }


    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

public class BookTest {
    public static void main(String[] args) {
        Book book = new Book();
      
       // 使用 set/get 访问成员变量
        book.setBookName("红楼梦");
        book.setPrice(12.2);
        
        System.out.println("book name is "+book.getBookName()+
                           ", price is "+book.getPrice());
    }
}

构造器

new 对象,并在 new 对象的时候为实例变量赋值

语法格式

[修饰符] class 类名{
 	[修饰符] 构造器名(){
 	// 实例初始化代码
 	}
  [修饰符] 构造器名(参数列表){
   // 实例初始化代码
   }
}

例如

public class Cat{
  private String catName;
  private int age;
  public Cat(String catName, int age){
    this.catName = catName;
    this.age = age;
  }
}
public class catTest{
  public static void main(String[] args) {
    //调用无参构造创建
    Cat cat1 = new Cat(); 	
    //调用有参构造创建
    Cat cat2 = new Cat("喵喵",12);
  }
}

说明:

  • 构造器名必须与它所在的类名必须相同
  • 它没有返回值,所以不需要返回值类型,也不需要 void
  • 构造器的修饰符只能是权限修饰符,不能被其他任何修饰。比如,不能被 static、 final、synchronized、abstract、native 修饰,不能有 return 语句返回值
  • 当我们显式的定义类的构造器以后,系统就不再提供默认的无参的构造器了。 在类中,至少会存在一个构造器。 构造器是可以重载的

参考资料

尚硅谷Java基础