概述
面向对象的三大特征
- 封装 (Encapsulation)
- 继承 (Inheritance)
- 多态 (Polymorphism)
面向对象的思想概述
类(Class)和对象(Object)是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)
可以理解为:类 = 抽象概念的人;对象 = 实实在在的某个人
面向对象程序设计的重点是类的设计;而类的设计,其实就是类的成员的设计。
java类
类的语法格式
修饰符 class 类名 {
属性声明;
方法声明;
}
类的成员构成
- 属性
- 构造器
- 方法
- 代码块
- 内部类
对象创建和使用
要想创建一个对象并且使用,首先要进行类的创建:
// 类的创建
class Person{
// 属性
String name;
int age;
// 方法
public void grown(){
age++;
}
public void greeting(){
System.out.println("Domo, my name is "+ name +", nice to meet you!");
}
}
有了类之后,便可以通过类名创建对象,并且通过" . "的方式来访问对象中的属性和方法:
public static void main(String[] args) {
// 创建对象
Person p1 = new Person();
// 调用属性
p1.name = "Kana";
p1.age = 14;
System.out.println(p1.name);
System.out.println(p1.age);
// 调用方法
p1.greeting(); // Domo, my name is Kana, nice to meet you!
p1.grown();
System.out.println(p1.age); // 15
}
完成一个对象创建后,系统会给对象的属性赋予默认的初始值;同时,对象之间的直接复制,本质上是将两个对象指向了同一个地址值。这点跟数组的情况非常类似:
// 对象内属性的默认初始化值
Person p2 = new Person();
System.out.println(p2.name); // null
System.out.println(p2.age); // 0
// 对象复制(类似数组)
Person p3 = new Person();
p3 = p1;
p3.name = "Yuki";
System.out.println(p1.name); // Yuki
以上代码的内存解析图如下:
内存解析图
匿名对象
可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
new Person().shout();
使用情况:
- 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 匿名对象可作为实参传递给一个方法进行调用。
比如,先声明一个Phone的类:
class Phone{
// 属性
double price;
// 方法
public void sendEmail(){
System.out.println("Send E-mail...");
}
public void playGame(){
System.out.println("Game Start!");
}
public void showPrice(){
System.out.println(price);
}
}
通过匿名对象的形式,调用其方法:
// 匿名对象
// 每一个匿名对象,都会是一个单独的实例
new Phone().playGame();
new Phone().price = 1999;
new Phone().showPrice(); // 0.0
这里需要注意,每一个匿名对象,都是一个单独的实例;即便上述代码通过调用匿名对象方法对其price属性进行赋值,但是通过调用匿名对象方法打印出来的price值依旧是0.0,这说明两次调用方法的不是同一个实例。
接着,再声明PhoneMall的类,它没有属性值,仅有一个通过Phone创建的实例作为参数、调用其方法的方法:
class PhoneMall{
public void showPhone(Phone phone,double price){
phone.price = price;
System.out.println(phone.price);
phone.sendEmail();
phone.playGame();
}
}
Phone的匿名对象作为实参,传递给方法进行调用。以下例子是通过这样的操作完成对匿名对象的price属性进行修改:
// 匿名对象的使用
PhoneMall mall = new PhoneMall();
mall.showPhone(new Phone(),2999); // 作为形参进行值传递,调用方法或者更改属性
类的属性
属性是类的成员之一,同时作为类的变量存在
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值,这点与数组十分类似:
对象属性的初始化值
变量(补充)
变量分为局部变量和全局变量(成员变量),类的属性就属于全局变量的一种;它们之间详细的分类和区别如下:
变量
结合之前类的属性、以及方法的形参进行比较,局部变量和全局变量的区别如下:
局部变量与全局变量
类的方法
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
- 将功能封装为方法的目的是:可以实现代码重用,简化代码。
- Java里的方法不能独立存在,所有的方法必须定义在类里。
class Customer{
// 属性
String name;
int age;
// 方法
public void intro(){ // 无形参、无返回值
System.out.println("Hello, nice to meet you");
}
public void years(int year){ // 有形参、无返回值
age += year;
return; // 可省略,表示结束此方法
}
public String getName(){ // 无形参、有返回值
return name;
}
public String getNation(String nation, String district){ // 有形参(形参列表)、有返回值
return nation + "," +district;
}
}
方法的定义形式跟其返回值类型和参数有直接关系,在通过对象调用方法时,也需要根据返回值类型和参数注意其格式:
// 创建实例
Customer cus1 = new Customer();
cus1.name = "Tom";
cus1.age = 24;
cus1.intro();
cus1.years(10);
System.out.println(cus1.age); // 34
System.out.println(cus1.getName());
System.out.println("I am coming from " + cus1.getNation("China","Chongqing"));
注意:
- 方法被调用一次,就会执行一次。
- 没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
- 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
- 方法中只能调用方法或属性,不可以在方法内部定义方法。
- 方法结束后,方法中定义的局部变量会被释放掉。
方法重载
- 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
- 与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
以下三个方法彼此之间构成重载:
public int getSum(int i,int j){
return (i + j);
}
public double getSum(double i,double j){
return (i + j);
}
public String getSum(String i,String j){
return (i + j);
}
// public int getSum(int i,int j){ // 报错
// return 0;
// }
可变个数的形参
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
public void show(int i){
System.out.println(i);
}
public void show(String i){
System.out.println(i);
}
public void show(String ...strings){ // 未指定参数数量
String str = "";
for(int i = 0; i < strings.length; i++){
str += strings[i];
}
System.out.println(str);
}
// public void show(String[] strings){ // 本质上是数组形式
// }
可变形参方法与其他同名方法之间构成重载。
值传递机制
- 若参数是基本数据类型,实参赋给形参的是实参真实存储的数据值
- 若参数是引用数据类型,实参赋给形参的是实参存储数据的地址值
比如以下类中,第一个方法的形参为基本数据类型,第二个方法的形参为引用数据类型,它们同样是执行交换数据的功能:
class Object{
// 属性
int m;
int n;
// 方法
public void swap(int m ,int n){
int temp = m;
m = n;
n = temp;
}
public void swap(Object obj){
int temp = obj.m;
obj.m = obj.n;
obj.n = temp;
}
}
// 若参数是基本数据类型,实参赋给形参的是实参真实存储的数据值
int m = 10;
int n = 20;
Object obj = new Object();
obj.swap(m,n);
System.out.println("m = "+ m +";n = " + n); // m = 10;n = 20
// 若参数是引用数据类型,实参赋给形参的是实参存储数据的地址值
obj.m = 10;
obj.n = 20;
obj.swap(obj);
System.out.println("obj.m = "+ obj.m +";obj.n = " + obj.n); // obj.m = 20; obj.n = 10
然而两个方法的执行结果却大相径庭,采用基本数据类型作为形参的方法,其实参传递给形参后进行交换,完全不会影响到对象内的属性值;而采用引用数据类型作为形参的方法,因为直接交换的地址值,所以其指向的对象也会发生实质性的交换。
递归方法
递归方法:一个方法体内调用它自身
- 1、方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
- 2、递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
比如:
// 利用递归累积求和
public int getSum(int n){
if(n == 1){
return 1;
}else{
return n + getSum(n-1);
}
}
当调用getSum(100)时,因为内部存在递归方法,就会调用 getSum(99) ;同理,当调用 getSum(99) 时,就会调用 getSum(98) ... ... 以此类推,最终总会调用 getSum(1) = 1 这个已知的条件,从而按原来相反的顺序返回 getSum(100) 的值。
封装与隐藏
隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
比如,声明一个Animal类,并使其legs属性为私有的,如需通过对象对该属性使用,则需要在类中声明访问私有属性的方法 getXxx() 和 setXxx() 。
class Animal{
// 属性
String name;
int age;
private int legs; // 腿的个数
// 方法
// 属性的设置与获取
public void setLegs(int l){
if(l >= 0 && l % 2 ==0){
legs = l;
}else{
legs = 0;
}
}
public int getLegs(){
return legs;
}
// 其它函数
public void eat(){
System.out.println("eating...");
}
public void show(){
System.out.println("name:"+ name +" age:"+ age +" legs:"+ legs);
}
}
创建Animal类的实例后,无法再用 “.” 的方式访问其私有属性。而应该使用事先声明好的 getXxx() 和 setXxx() 方法:
Animal a = new Animal();
a.name = "dog";
a.age = 2;
// a.legs = 4; // 属性不可见/隐藏,不可直接调用
a.setLegs(4);
System.out.println(a.getLegs());
a.show();
权限修饰符
Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
权限修饰符
类的构造器
类的构造器用来创建对象;并给对象进行初始化。
比如声明一个Student类:
class Student{
// 属性
private String name;
private int age;
// 构造器
public Student(){ // 系统提供的默认构造器
}
public Student(String n){
name = n;
}
public Student(String n, int a){
name = n;
age = a;
}
// 方法
public void show(){
System.out.println("Name:"+ name +" Age:" + age);
}
}
- 如没有事先显示定义构造器,则系统默认提供一个空参数的够造器
- 一旦显示定义了构造器,系统就不会提供默认构造器
- 一个类中可以定义多个构造器,彼此构成重载
Student stu = new Student();
stu.show();
Student stu1 = new Student("Kana", 16);
stu1.show();
构造器特征
- 它具有与类相同的名称
- 它不声明返回值类型。(与声明为void不同)
- 不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值
关键字(this、package、import)
this
当this在方法内部使用,即这个方法所属对象的引用;
当this在构造器内部使用,表示该构造器正在初始化的对象。
当在方法内需要用到调用该方法的对象时,就用this。
具体的:我们可以用this来区分属性和局部变量。
比如:this.name = name;
class Person{
// 属性
private String name;
private int age;
// 方法
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public void show(){
System.out.println("Name:"+this.getName()+" Age:"+this.getAge());
}
}
this修饰构造器:
// 构造器
// this修饰/调用构造器
public Person(){
// 假设对象初始化,必须输出以下字段
System.out.println("this is an important sentence!");
}
public Person(String name){
this();
this.name = name;
}
public Person(int age){
this();
this.age = age;
}
public Person(String name,int age){
this(age); // 通过this(field)来调用其他指定的构造器
this.name = name;
}
package
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。
package project.java;
系统文件夹会创建对应的路径:
系统文件夹
import
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。import语句告诉编译器到哪里去寻找类。
package practice.java;
import project.java.*;
public class Test {
public static void main(String[] args) {
CustomerView cs = new CustomerView();
cs.enterMainMenu();
}
}
通过import,在当前包(practice)中导入另一个包(project)的项目工程。
网友评论