Hello,这是本平台的第一篇文章,如果喜欢,欢迎多多支持呦!
类和对象
面向对象程序设计(OOP) 想要理解OOP,必须先理解类和对象。
- 类 是构造对象的模板,对象 是类的实例。
类作为对象的规范而存在,其中有成员变量和成员函数,可以认为成员函数中包括构造函数,而通过构造函数可以构造一个对象。
构造函数应与类名相同,所以我们会看到这样的一幕:
Scanner in = new Scanner (System.in);Scanner ()就表示调用了一个构造函数,括号里的System.in只不过是往构造函数中传入了一个参数。 这同时也为我们引出了对象······ - 我们使用
new+构造函数创建对象。其中in是对象变量,但对象变量是对象的管理者,并非对象本身。
关于对象的定义,在《Java核心卷》中提到:定义对象应先了解对象的三个特征。 1. 对象的行为-通过方法定义 2. 对象的状态-状态的改变通过调用方法实现 3. 对象的标识-作为类的实例,每个对象的标识都不同 例如:我们熟悉的
in.nextInt();就是对象变量调用了Sanner类的方法(函数)nextInt。这就定义了对象的行为,可以让对象为我们做事。
上文提到的方法,就是类中的成员函数。想要创建自己的类,首先应该明白以下三条书写规则。
- 构造函数:
public +类名 () {} - 成员变量:
private +变量类型+变量名称 - 成员函数:
public +函数类型 +函数名称() {}
访问修饰符
大家可能会注意到前面的public/private,这是Java访问修饰符。 权限顺序:public>protected>default>private
- public: 使变量/函数在任何地方都可以被访问。
- protected: 表示同一个包内可以访问,以及不在同一个包中的子类。
- default: 表示同一个包内可以访问。
- private: 只能用于成员变量和成员函数,仅限于一个类中。
最后来简单介绍封装,封装可以被认为是一个保护屏障,防止当前类的代码和数据被外部类定义的代码随机访问,一般使用private即可实现这种功能。
还要明确以下几点规则:
- 在一个源文件(.java)中,只能有一个公有(public)类,但可以有无数个非公有类。
- public类名要与源文件名相同,构造函数要与类名相同。
- 程序中会存在默认构造器。
成员变量与成员函数
类由成员变量和成员函数组成。 类定义了对象中所具有的变量,这些变量称为成员变量。每一个对象都有自己的变量,和同一类的其他对象是分开的,所以成员变量的生存期是对象的生存期,作用域是类内部的成员函数。
package、import
- package
Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
Java是用文件夹去管理包的。包的名字可以加
.来表明文件夹的层次。 - import
为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 "import" 语句可完成此功能。import 可以提供一个路径,使得编译器可以找到某个类。
比如在使用Scanner类之前通常会有一句
import java.util.Scanner;如果不用import,就需要写出类的全名:(包.类)
静态域与静态方法
-
具有static修饰符的变量: 具有
static修饰符的变量叫做类变量(静态变量)。 表明变量不在任何对象中,而是在类中,且只有一个。 而每一个对象对于所有的实例域却都有自己的一份拷贝。 -
具有static修饰符的函数: 具有
static修饰符的函数叫做类函数。表明函数不属于任何对象,而是属于这个类。,static函数和static函数之间可以访问。 -
关于static函数的调用: 静态(static)方法可以不用创建对象就调用。 注:在调用时,通过类调用或通过对象调用都是相同的,但更推荐通过类调用类函数/类变量。
补充:程序执行时发生了什么?
1. 静态优先,构造随后。 static修饰的成员变量和方法是属于类的,Java中的静态代码块是在虚拟机加载类的时候就执行的,而且只执行一次。而普通变量是属于对象的,调用类的构造方法是在new一个对象的时候执行的。 2. 先父再子。 由于子类继承了父类,就拥有了父类的所有属性和方法(除了构造方法),在创建子类对象时,先会执行构造函数的第一句super()来调用父类对应的构造方法。所以,会先向上追溯到Object,然后在依次向下执行类的初始化块和构造方法,直到当前子类为止。
隐式参数和显式参数
在程序示例1-1中,使用了this关键字。在执行void getName(String n)方法时,该方法存在两个参数,一个是隐式参数,就是在调用方法前创建的dog对象。如果不使用this,name=n将会调用Dog类的对象dog的实例域name设置为值n,dog.name=n。
如果使用this,this就会表示该隐式参数。
另一个是显式参数,也就是在方法括号中的String n。
程序示例1-1
public class Main {
public static void main(String[] args) {
//new一个对象,交给dog来管理。
Dog dog=new Dog(12,"red",1,"dog");
//使用对象变量调用方法
dog.getName();
}
}
class Dog {
//成员变量
private int size;
private String colour;
private int age;
private String name;
//构造函数(无传入参数)
public Dog(){
}
//构造函数(有传入参数)
public Dog(int size, String colour, int age, String name) {
this.size = size;
this.colour = colour;
this.age = age;
this.name = name;
}
//以下为成员函数,表示这个类存在的方法,表示类可以调用的方法。
void getName(){
System.out.println(name);
}
@Overload
void getName(String n){
this.name=n;
System.out.println(name);
}
}
继承(extends)
面向对象程序设计语言有三大特性:封装、继承和多态性。前面已经介绍过封装,接下来我们来介绍继承。
- 什么是继承? 继承是一个is-a关系,表示父类与子类之间的关系,子类通过继承父类所有非私有的方法,从而达到重复使用父类数据和方法的效果。
- 为什么要用继承? 降低代码的复用性。需要注意的是,Java并不支持多继承(一个子类继承两个父类),但是可以通过接口来实现多继承。
继承关键字
final关键字
final修饰变量:final实例域一般和static一起使用,修饰变量时,表示变量不可以进行修改。 final 修饰类:说明该类不允许任何类继承。 final修饰方法,该方法不能被子类重写。
super关键字
super有两个用途,一个是调用父类的构造器,一个是调用父类的方法。
- 作为前者 当父类的构造器无传参时,构造子类对象时会先进行父类的定义初始化和调用构造器。父类构造器有传参时,则必须使用super关键字来表示调用父类的构造器。
- 作为后者
若子类和父类中有同名的函数,可以使用
super和.调用父类的函数。 参考程序实例2-1
instance of关键字
instance of通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
判断LinkedHashMap是否为HashMap的子类?
boolean o=false;
LinkedHashMap linkedHashMap=new LinkedHashMap();
o=linkedHashMap instanceof HashMap;
System.out.println(o); //true
程序示例2-1
测试类
public class ManagerTest {
public static void main(String[] args) {
Employee employee=new Employee("Wang",1000);
System.out.println(employee.getSalary());
}
}
Manager子类继承Employee类
//extends关键字,表示Manager方法继承于Employee。
class Manager extends Employee{
private double salary;
public Manager(String name,double baseSalary,double salary){
//使用super关键字,调用父类构造器。
super(name,baseSalary);
this.salary=salary;
}
//子类重写父类方法
@Override
public double getSalary() {
//使用super关键字,调用父类方法。
return super.getSalary()+salary;
}
}
}
Employee父类
class Employee{
public String name;
public double baseSalary;
public Employee(String name,double baseSalary){
this.name=name;
this.baseSalary=baseSalary;
}
public double getSalary(){
return baseSalary;
}
重写(Override)& 重载(Overload)
@Override
- 定义 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
- 使用 Java的多数类都重写了Object类中的方法。
实现重写,子类能够根据需要实现父类的方法。
@Overload
- 定义 一个类中定义了多个方法名相同,而参数数量/类型/次序不同的方法,则称为方法的重载(Overload)。
- 使用 在编写类时,一般会写一个空参的构造器,和一个带参的构造器。这里就使用到了重载。
方法重载(Overload)是一个类的多态性表现,而方法重写(Override)是子类与父类的多态性表现。
Object类
Java中所有的类都是从Object继承的,也就是所有类的父类。进而可知,所有其他类都可以使用/重写Object类的方法。
- 常用的Object类的方法
toString方法,用来显示对象的字符串形式。equal()方法,在Object类中的equals方法只是简单地比较地址是否相等。 而多数Java类中实现了重写equals方法,使其比较内容是否相等。在比较两个对象是否相等时,一般使用equals方法,当然前提是对象所属的类已经重写equals了。
多态
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
- 多态的前提 继承+重写+向上造型。 Java的对象变量是多态的,能保存不止一种类型的对象。
- 向上造型(多态的体现) 父类引用指向子类对象。
- 多态的好处 可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
这里就使用了父类引用指向子类对象(向上造型)。实现向上造型必须满足两个条件:
- 在编译阶段,只是检查参数的引用类型,把多态的对象变量
manager看作是父类类型。 - 可以调用那些方法看父类,而方法具体执行看子类是否重写。
简单来说,就是编译看左,运行看右。
抽象(Abstract)和接口(Interface)
- 抽象 父类只是定义了一个抽象函数的概念,而具体的方法要交给子类实现。
- 抽象的两个规则 如果一个方法是抽象的,那么它的类也应该是抽象的。 如果父类是抽象的,子类必须实现父类的抽象方法或变为抽象类。
在这里需要注意,抽象类是不能进行实例化的。
- 接口 接口就是抽象方法的集合。在学习中,经常把接口和抽象类放在一起进行学习。
文章链接:接口和抽象类的区别
程序示例3-1
测试类
public class Text {
public static void main(String[] args) {
//父类引用指向子类对象。
Person p =new Student(12,"Sivan",123);
Student s=new Student(12,"Sivan",123);
System.out.println(p.getName()); //Sivan
//编译不通过,因为父类中没有此方法。
// p.getNumber();
//编译通过,运行通过
System.out.println(s.getNumber()); //123
p.eat();
s.eat();
System.out.println(p.equals(s)); //true
}
}
function接口
//定义接口,接口中的方法都是抽象的
interface function{
public void eat();
public void walk();
}
Person类
//类调用接口
class Person implements function{
private int age;
private String name;
//实现两个构造器,重载的实例
public Person(){
}
public Person(int age,String name){
this.age=age;
this.name=name;
}
public String getName(){
return name;
}
//实现接口的功能
public void eat(){
System.out.println("EAT");
}
public void walk(){
System.out.println("WALK");
}
}
Student子类,继承Person
class Student extends Person {
private int number; //学号
public Student(int age, String name, int number) {
super(age, name);
this.number = number;
}
public int getNumber() {
return number;
}
//重写接口方法
@Override
public void eat(){
System.out.println("Student EAT");
}
//重写Object类的方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return number == student.number;
}
}
枚举类
Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。 Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。
枚举类的使用
- 枚举类的使用说明
- 类的对象只有有限个,确定的。
- 需要定义一组常量时,建议使用枚举类。
- 如果枚举类只有一个对象,可以看作是单例模式的实现方式。
- 自定义枚举类
- jdk5.0之前,自定义。程序示例:EnumText1.java
- jdk5.0之后,使用enum关键字定义枚举类。程序示例:EnumText2.java
- 枚举类中的方法 程序示例:EnumText2.java
- 枚举类使用接口的情况 程序示例:EnumText2.java
最后的补充:Junit测试方法 JUnit是单元测试的基础单元测试框架。每编写完一个函数之后,都应该对这个函数的方方面面进行测试。 在每个方法前加入@Test,alt+enter即可导入Junit进行单元测试。