面向对象(Java)
一、概念
1.首先,我们要了解面向对象,必须要了解第一件事情,java面向对象说:万事万物皆对象
2.我们认知对象,是通过自己对这个世界的统一认识出发的。其中包括提前对于某个对象归属于那个类型的定义
比如说,当我们看到一个手机,由于这个手机符合我们对手机类型的全定义,所以我交这个东西叫手机
3.而我们创建一个相似对象的分类时,使用的“工具”叫做抽象,就是描述着一类对象的名词,当做对象的属性,
将描述这一类对象的动词,当做方法来提取一个模板,任何符合这个模板定义的对象都属于这个类型的对象
4.这个模板,在java中,叫做类。而我们说的万事万物皆对象,就是如何用一个类型创建这一个类的对象
java中:对象建立
①、首先的定义一个类,里头有对象抽象出来共有的属性和方法。eg,手机类
//类:是用来描述对象的模板,是通过抽象和封装过程得到的。
public class CellPhone {
//成员变量:组成类型的变量
float weight;
String color;
String brand;
int price;
//方法 格式:
//返回值类型 方法名(参数列表){代码块}
//return 结束方法,返回值
boolean callNumber(String num) { //形式参数 ph.callNumber("156165"); //其15..为实际参数·
System.out.println(num);
return true;
}
}
②、创建对象:可以赋值,也可以调用方法
CellPhone ph = new CellPhone();
ph.brand = "小米";
ph.color = "黑色";
ph.price = 900;
ph.weight = 100.0f;
System.out.println(ph.weight);
注意:如果没有赋值,系统会给对象自动有默认值
int -> 0 , float -> 0.0 ,char -> 空格 ,String -> null
null 与 "“的区别:”"代表有一个string对象,只是里头啥都没有,而null就是虚无,什么都没有
二、JAVA的内存
1、Java的内存分为5个部分:
分别是:程序计数器:记录程序当前执行的行数
本地方法栈:记录Native方法的执行流程
虚拟机栈:记录JVM虚拟机的执行流程
堆:保存实际对象的位置
方法区:静态变量和常量以及类模型等内容所在位置
2、虚拟机栈和堆:
虚拟机栈,当我们的程序,开始运行的时候,虚拟机(JVM)就将内存已经分配好了。
这个时候,JVM给我们了一小片空间来记录当前的代码执行在那些方法中,那些属性记录,这些东西都在栈这个空间中。
但是面向对象语言,有一个特别的特征,就是需要对象去操作业务流程,而栈的大小不能满足放置多个对象,所以我们需要将对象放置在堆中。
如果栈里面需要执行的代码,要用到堆里的对象,怎么办?
栈需要记录这个对象在堆里面的位置,并不记录这个对象
什么时候在堆里面创建对象呢?什么时候在栈里面申明引用呢?
我们把JAVA代码中,这一行,拆解一下: Item item = new Item();
这一行,其实是两个动作的简写,分别是声明和赋值:
声明:Item item;当代码在声明时,JVM会在栈中一个叫做栈帧的东西里面创建一个指向空白的引用(就是C语言的指针),这个空白的引用,指向了null
赋值:当我们写了item = new Item();这个动作又能分为两个部分,第一个部分,叫做对象的创建,就是new Item()
new这个关键字,最主要的行为,就是在堆里面划分一片合理的内存空间,用来保存创建的对象。当我们写了 = 号时,这个时候,栈里面的引用就可以和堆里面的内存空间,关联上了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FEPw9zPw-1617612879439)(C:\Users\11141\AppData\Roaming\Typora\typora-user-images\image-20210331201919962.png)]
三、访问控制
访问控制,就是在创建对象以后,这个对象的属性或者方法是否可以被访问到
读取到(有些属性可能你具有,但是别人是获得不到的)
不写访问控制符时,只在包里头可以调用
1、public 公有
可以有公有属性、公有方法
public int height;
public void test(){
}
公有:在项目内任何位置对象都可以访问或调用。
2、private 私有
可以有公有属性、公有方法
private int height;
private void test(){
}
私有:在类外面无法调用或访问,只可以在类中自己用,私有方法一般用于简写代码,类中其他方法调用。
3、默认访问控制符(default)
int height;
void test(){
}
表示:可以在包内访问或调用。
4、protected 友元访问
包括继承和同包,继承的时候即使在不同包的时候也可以调用(对象不可调用,只能类中直接调用)
protected void eat() {
}
5、总结:主要是调用范围
public-protected-default-private
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3iPL866n-1617612879443)(C:\Users\11141\AppData\Roaming\Typora\typora-user-images\image-20210401194815063.png)]
四、构造方法
1、方法名与类名一致,大小写敏感
2、方法不能有返回值的描述,void也不可加,加上就不是构造方法了
3、这个方法一般在new这个关键字后调用的,是用来创建这个类的对象的
4、如果我们不写这个方法,那么java会给出一个没有参数,没有方法的默认的构造方法
5、也就是说,任何的类中,都有构造方法
public Hero() {
}
public Hero(String nameString,int currentHP,int maxHP,int attackPoint,int attackDefendPoint) {
this.attackDefendPoint = attackDefendPoint;
this.attackPoint = attackPoint;
this.currentHP = currentHP;
this.nameString = nameString;
this.maxHP = maxHP;
}
注:一般情况下私有属性由构造方法定义初值,如上代码,构造方法可以重载,即参数列表需要不同。
构造方法(继承中)
子类构造方法是必须先调用父类的构造方法,super(i)
若父类有无参的构造方法(默认调用),子类就可以不用一定要调用父类的构造方法,即super()
调用有参构造就只运行父类的有参构造
子类构造方法中只能调用一个父类构造方法
public ClassA {
public ClassA(int i){
}
}
public ClassB extends ClassA {
public ClassB(int i){
super(i);
}
//必须优先调用父类构造方法,若父类构造方法没有重载,则可以默认为没有参数的构造方法
//并且只可以调用一个父类构造方法,不能一次调用多个父类构造方法,会造成冲突(优先问题)
}
五、继承(扩展)
java在类中只支持链式继承、单继承(extends后不能有多个类)
关键字为:extends —即扩展
//例子,以下代码都用这个举例
public ClassA {
}
public ClassB extends ClassA {
}
其中ClassA成为父类(基类、超类),ClassB为子类(延展类、sub类)
里氏替换原则
在任何需要父类对象的时候,都可以拿子类对象替换
eg: ClassA a = new ClassB();
子类在继承后可以添加自己特有的属性或方法,所以子类对象在堆里头的空间一定是包括一个父类对象,因此可以拿子类对象替换父类对象。
但声明时还是为父类对象,不可以调用子类中的特有方法,可以通过强转类型来调用,但不建议。
六、重写与重载
1、重载
重载,重载过程是方法名一致,但是方法的参数列表的类型、个数、顺序三个方向考量,是不一致的,这个时候,我们将这两个同名的方法叫做重载。返回类型不可以进行重载,就是必须参数列表不同。即只要在本类中即可重载。
2、重写(override)
子类中重新定义父类中已经定义过的方法的过程叫做重写,重写的英文叫做override
public ClassA {
void hehe(){
}
}
public ClassB extends ClassA {
//@Override为注解,是一种编程方式,下面这个注解的作用是告诉编译器,eat这个方法是从父类继承过来的
//并且在本类中重新实现的,约束在父类中必须要有,不然会报错,可以省略就不约束了。
@Override
void hehe(){
//自动生成的代码块,养成习惯删掉此行注解
// TODO Auto-generated method
}
}
注意:
(1)、重写方法时类型与参数列表都得一致
(2)、重写方法或属性时,要注意访问控制符,子类只能对父类的访问限制往大里变
由小到大:private - default - protected - public
七、this
this 是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。
this 的用法在 Java 中大体可以分为3种:
1.普通的直接引用
这种就不用讲了,this 相当于是指向当前对象本身。
2.形参与成员名字重名,用 this 来区分:
实例
class Person {
private int age = 10;
public Person(){
System.out.println("初始化年龄:"+age);
}
public int GetAge(int age){
this.age = age;
return this.age;
}
}
public class test1 {
public static void main(String[] args) {
Person Harry = new Person();
System.out.println("Harry's age is "+Harry.GetAge(12));
}
}
运行结果:
初始化年龄:10
Harry's age is 12
可以看到,这里 age 是 GetAge 成员方法的形参,this.age 是 Person 类的成员变量。
八、super
super 可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。
super 也有三种用法:
1.普通的直接引用
与 this 类似,super 相当于是指向当前对象的父类,这样就可以用 super.xxx 来引用父类的成员。
2.子类中的成员变量或方法与父类中的成员变量或方法同名
实例
class Country {
String name;
void value() {
name = "China";
}
}
class City extends Country {
String name;
void value() {
name = "Shanghai";
super.value(); //调用父类的方法
System.out.println(name);
System.out.println(super.name);
}
public static void main(String[] args) {
City c=new City();
c.value();
}
}
运行结果:
Shanghai
China
可以看到,这里既调用了父类的方法,也调用了父类的变量。若不调用父类方法 value(),只调用父类变量 name 的话,则父类 name 值为默认值 null。
3.引用构造函数
- super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
- this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。
实例
class Person {
public static void prt(String s) {
System.out.println(s);
}
Person() {
prt("父类·无参数构造方法: "+"A Person.");
}//构造方法(1)
Person(String name) {
prt("父类·含一个参数的构造方法: "+"A person's name is " + name);
}//构造方法(2)
}
public class Chinese extends Person {
Chinese() {
super(); // 调用父类构造方法(1)
prt("子类·调用父类"无参数构造方法": "+"A chinese coder.");
}
Chinese(String name) {
super(name);// 调用父类具有相同形参的构造方法(2)
prt("子类·调用父类"含一个参数的构造方法": "+"his name is " + name);
}
Chinese(String name, int age) {
this(name);// 调用具有相同形参的构造方法(3)
prt("子类:调用子类具有相同形参的构造方法:his age is " + age);
}
public static void main(String[] args) {
Chinese cn = new Chinese();
cn = new Chinese("codersai");
cn = new Chinese("codersai", 18);
}
}
运行结果:
父类·无参数构造方法: A Person.
子类·调用父类”无参数构造方法“: A chinese coder.
父类·含一个参数的构造方法: A person's name is codersai
子类·调用父类”含一个参数的构造方法“: his name is codersai
父类·含一个参数的构造方法: A person's name is codersai
子类·调用父类”含一个参数的构造方法“: his name is codersai
子类:调用子类具有相同形参的构造方法:his age is 18
从本例可以看到,可以用 super 和 this 分别调用父类的构造方法和本类中其他形式的构造方法。
例子中 Chinese 类第三种构造方法调用的是本类中第二种构造方法,而第二种构造方法是调用父类的,因此也要先调用父类的构造方法,再调用本类中第二种,最后是重写第三种构造方法。
九、Java 抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
1、抽象类
在Java语言中使用abstract class来定义抽象类。如下实例:
public abstract class Employee
{
private String name;
private String address;
private int number;
public Employee(String name, String address, int number)
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay()
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck()
{
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString()
{
return name + " " + address + " " + number;
}
public String getName()
{
return name;
}
public String getAddress()
{
return address;
}
public void setAddress(String newAddress)
{
address = newAddress;
}
public int getNumber()
{
return number;
}
}
2、继承抽象类
我们能通过一般的方法继承Employee类:
Salary.java 文件代码:
public class Salary extends Employee
{
private double salary; //Annual salary
public Salary(String name, String address, int number, double
salary)
{
super(name, address, number);
setSalary(salary);
}
public void mailCheck()
{
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
public double getSalary()
{
return salary;
}
public void setSalary(double newSalary)
{
if(newSalary >= 0.0)
{
salary = newSalary;
}
}
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
尽管我们不能实例化一个 Employee 类的对象,但是如果我们实例化一个 Salary 类对象,该对象将从 Employee 类继承 7 个成员方法,且通过该方法可以设置或获取三个成员变量。
AbstractDemo.java 文件代码:
public class AbstractDemo
{
public static void main(String [] args)
{
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
以上程序编译运行结果如下:
Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.
3、抽象方法
如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
public abstract class Employee { private String name; private String address; private int number; public abstract double computePay(); //其余代码 }
声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
如果Salary类继承了Employee类,那么它必须实现computePay()方法:
Salary.java 文件代码:
public class Salary extends Employee
{
private double salary; // Annual salary
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
//其余代码
}
抽象类总结规定
- 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
如果Salary类继承了Employee类,那么它必须实现computePay()方法:
Salary.java 文件代码:
public class Salary extends Employee
{
private double salary; // Annual salary
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
//其余代码
}