1.面向对象的思想:
面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想是将数据作为第一位,而方法或者说是算法作为其次,这是对数据一种优化,操作起来更加的方便,简化了过程。
2.类和对象的关系:
类与对象时整个面向对象中最基础的组成单元。
类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
对象:对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。
可以一句话来总结出类和对象的区别:类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。
2.1类与对象的定义和使用:
在Java中定义类,使用关键字class完成。语法如下:
class 类名称 {
属性 (变量) ;
行为 (方法) ;
}
范例:定义一个Person类
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
类定义完成之后,肯定无法直接使用。如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式(两种格式)如下:
(1)格式一:声明并实例化对象
类名称 对象名称 = new 类名称 () ;
(2)格式二:先声明对象,然后实例化对象:
类名称 对象名称 = null ;
对象名称 = new 类名称 () ;
引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。
当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
对象.属性:表示调用类之中的属性;
对象.方法():表示调用类之中的方法
范例:使用对象操作类
package com.wz.classandobj;
class Person {
String name ;
int age ;
public void get() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;// 声明并实例化对象
per.name = "张三" ;//操作属性内容
per.age = 30 ;//操作属性内容
per.get() ;//调用类中的get()方法
}
}
3.面向对象的三大特征之封装
3.1封装的概念
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
3.2访问控制符的使用
Java提供了4种访问控制级别,发别通过public,protected,默认访问控制符 (default)、private来进行修饰
public:public是公共的意思,被public修饰的成员可以被所有类访问
protected:protected修饰的成员既可以被同一个包中的其他类访问,也可以被不同包中的子类访问
default:如果在类的成员前没有任何访问控制符,则该成员处于默认访问状态。处于默认访问状态的成员,只能被同一个包中的其它类访问
private:private修饰的成员只能被这个类本身访问,其它类(包括同一个包中的类、其他包中的类和子类)都无法访问private修饰的成员
范例:封装一个student类
public class Student {
private int stuId;
private String stuName;
private String stuSex;
private int stuAge;
private String stuAddr;
//创建一个有参函数
public Student(int stuId, String stuName, String stuSex, int stuAge, String stuAddr) {
this.stuId = stuId;
this.stuName = stuName;
this.stuSex = stuSex;
this.stuAge = stuAge;
this.stuAddr = stuAddr;
}
//创建一个无参函数
public Student() {
}
//创建toString方法
@Override
public String toString() {
return "Student{" +
"stuId=" + stuId +
", stuName='" + stuName + '\'' +
", stuSex='" + stuSex + '\'' +
", stuAge=" + stuAge +
", stuAddr='" + stuAddr + '\'' +
'}';
}
//创建get,set方法
public int getStuId() {
return stuId;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
public String getStuName() {
return this.stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public String getStuSex() {
return this.stuSex;
}
public void setStuSex(String stuSex) {
this.stuSex = stuSex;
}
public int getStuAge() {
return this.stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public String getStuAddr() {
return stuAddr;
}
public void setStuAddr(String stuAddr) {
this.stuAddr = stuAddr;
}
}
4.面向对象的三大特征之继承
4.1继承的特点
1.继承(Inheritance)可以实现类之间共享属性和方法
2.继承可以最大限度地实现代码复用
3.定义:继承是在已有类的基础上构建新的类,一个类继承已有类后,可以对被继承类中的属性和方法进行重用
4.继承的实现:类的继承通过extends关键字来实现。
5.继承的层次
(1)Java中不允许多重继承,子类只能继承一个父类,即单一继承。
(2)Java可以有多层继承。即一个类可以继承某一个类的子类,从而形成一颗继承树。
4.2方法的重写
1.子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中实现不同,以实现不同于父类的功能,这种方式被称为方法重写。
2.方法重写时的注意事项:
(1)方法重写时,方法名与形参列表必须一致。
(2)方法重写时,子类的权限修饰符必须要大于或者等于父类的权限修饰符。
(3)方法重写时,子类的返回值类型必须要小于或者等于父类的返回值类型。
(4)方法重写时,子类抛出的异常类型要小于或者等于父类抛出的异常类型。
4.3 super关键字
1.super代表父类对象
2.super关键字用来在子类的成员方法中访问父类成员
3.super的用法:
(1)每一个子类的构造方法在没有显示调用super()时,系统都会提供一个默认的super。
(2)super()书写在第一行。
(3)在子类构造方法中显示调用super(),完成对特定父类构造方法的调用。
4.4 final关键字
1.在Java中,final可以修饰变量,也可以修饰类或者方法。
2.final修饰局部变量,final修饰的变量一旦被赋值,这个值不能被改变。如果对该变量进行再次赋值,则程序会在编译时报错。
3.final修饰成员变量,final修饰成员变量必须赋予初值
4.final修饰类意味着这个类不能被继承
5.final修饰方法意味着这个方法不能被重写
4.5 抽象类
1.抽类方法:(当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法是无法确定的。)Java允许在定义方法时不写方法体。不包含方法体的方法称为抽象方法。抽象方法使用abstract关键字来描述,具体示例如下:
public abstract 返回值 方法名(参数列表);
2.抽象类:当一个类中包含了抽象方法,该类必须用abstract关键字来修饰,使用abstract关键字来修饰的类称为抽象类。
3.定义抽象类时的注意事项:
(1)包含抽象方法的类必须声明为抽象类,但抽象类可以不包含任何抽象方法,只需要使用abstract关键字修饰即可。
(2)抽象类是不能被实例化的,因为抽象类中的抽象方法没有被实现,不能被调用。
范例:创建关于Manger和Coder的抽象类 Employee
public abstract class Employee {
private String name;
private String id;
private double money;
public Employee(String name, String id,double money) {
this.name = name;
this.id = id;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public abstract void work();
}
然后创建类Coder,并提供构造方法
public class Coder extends Employee{
public Coder(String name, String id, double money) {
super(name,id,money);
}
//姓名为:张三,工号为:9527,工资为:10000.0,的程序员正在编写代码
@Override
public void work() {
// TODO Auto-generated method stub
System.out.println("姓名为:" + super.getName()+ ",工号为:" + super.getId() +",工资为:"+ super.getMoney() +",的程序员正在编写代码");
}
}
然后创建Manger类,添加独有的成员变量,并提供构造方法
public class Manger extends Employee{
private double jangJiN;
public Manger(String name, String id, double money, double jangJin) {
super(name, id, money);
this.jangJiN = jangJin;
}
public double getJangJiN() {
return jangJiN;
}
public void setJangJiN(double jangJiN) {
this.jangJiN = jangJiN;
}
//姓名为:李四,工号为:9528,工资为:15000.0,奖金为:2000.0,的项目经理正在管理程序员写代码
@Override
public void work() {
// TODO Auto-generated method stub
System.out.println("姓名为:" + super.getName()+ ",工号为:" + super.getId() +",工资为:"+ super.getMoney()+"奖金为:"+this.getJangJiN() +"的项目经理正在管理程序员写代码");
}
}
5.面向对象的三大特征之多态
5.1.多态的概念
多态是面向对象程序设计的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。可以理解成,多态就是同一个接口,使用不同的实例而执行不同操作。也可以理解成,基类对象访问派生类的重写方法,在程序实际执行过程中表现为循环调用基类对象,访问不同的派生类。
5.2多态的实现
1.多态实现的三大前提:继承 重写(在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法,并且多态对象只能调用父类中定义子类中重写的功能,不能调用子类的特有功能) 父类引用变量可以指向子类对象 格式:父类类型 变量名=new 子类类型() 如:Parent p=new Child()
2.多态的转型:
向上转型:可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准
格式:父类类型 变量名=new 子类类型() 如:Parent p=new Child()
适用于:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型。相当于创建了一个子类对象一样,可以用父类的,也可以用自己的
格式:子类类型 变量名=(子类类型) 父类类型的变量 如:Child c = (Child)p;//此时的p应为Parent类型
适用于:当要使用子类特有功能时
5.3.多态的实现形式
在Java中有三种形式可以实现多态:继承和 接口 抽象类和抽象方法
基于继承实现的多态的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为
5.4.多态的优点
- 多态可以让我们不用关心某个对象到底具体是什么类型,就可以使用该对象的某些方法
- 提高了程序的可扩展性,可维护性 ,可替换性,灵活性等
public class Tea {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Tea(){
}
public String drink(){
return "喝的是 " + getName();
}
/*** 重写toString()***/
public String toString(){
return null;
}
}
public class Xihu extends Tea{
public Xihu(){
setName("Xihu");
}
/** 重写父类方法,实现多态**/
public String drink(){
return "喝的是 " + getName();
}
/*** 重写toString()***/
public String toString(){
return "Tea : " + getName();
}
}
public class Xinyang extends Tea{
public Xinyang(){
setName("Xinyang");
}
/** 重写父类方法,实现多态**/
public String drink(){
return "喝的是 " + getName();
}
/*** 重写toString()***/
public String toString(){
return "Tea : " + getName();
}
}
6.接口
6.1接口概述
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
6.2 接口定义
接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
6.2.1 含有抽象方法
- 抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体,要以分号结束。该方法供子类实现使用。
public interface InterFaceName {
public abstract void method();
}
6. 2.2 含有默认方法和静态方法(JDK1.8)
- 默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
- 静态方法:使用 static 修饰,供接口直接调用
public interface InterFaceName {
public default void method() {
// 执行语句
}
public static void method2() {
// 执行语句
}
}
6.2.3含有私有方法和私有静态方法 (JDK 9)
- 私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用
public interface InterFaceName {
private void method() {
// 执行语句
}
}
6.3 接口的实现
6.3.1 实现的概述
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。
实现格式:
class 类名 implements 接口名 {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【可选】
}
非抽象子类实现接口:
- 必须重写接口中所有抽象方法。
- 继承了接口的默认方法,即可以直接调用,也可以重写。
6.3.2 抽象方法的使用
要使用接口中的抽象方法,实现类中必须全部实现
- 定义接口
public interface LiveAble {
// 定义抽象方法
public abstract void eat();
public abstract void sleep();
}
- 定义实现类
public class Animal implements LiveAble {
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void sleep() {
System.out.println("晚上睡");
}
}
6.4 接口特点
- 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰,可省略不写,但不推荐省略
- 接口中,没有构造方法,不能创建对象。
- 接口中,没有静态代码块。
7.内部类
7.1 概述
7.1.1什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,类B则称为外部类。
例如:身体和心脏的关系。又如:汽车和发动机的关系。
7.1.2分类
成员内部类
局部内部类(包含匿名内部类)
7.2 访问特点
内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须要建立内部类的对象。
7.3 成员内部类
如果一个类是定义在一个方法外部的,那么这就是一个成员内部类。
-
成员内部类 :定义在类中方法外的类。
-
定义格式:
修饰符 class 外部类名称 {
修饰符 class 内部类名称 {
// ...
}
// ...
}
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。
比如,汽车类 Car
中包含发动机 类 Engine
,这时, Engine
就可以使用内部类来描述,定义在成员位置。
代码举例:
class Car { //外部类
class Engine { //内部类
}
}
使用方式
-
间接方式:在外部类的方法当中,使用内部类;然后
main
只是调用外部类的方法。 -
直接方式,公式:
类名称 对象名 = new 类名称();
定义类:
// 如果出现了重名现象,那么格式是:外部类名称.this.外部类成员变量名
public class Outer { // 外部类
// 外部类的成员变量
private int num = 10; // 外部类的成员变量
public class Inner { // 成员内部类
private int num = 20; // 内部类的成员变量
// 内部类的方法
public void methodInner() {
System.out.println("内部类的方法");
int num = 30; // 内部类方法的局部变量
System.out.println(num); // 局部变量,就近原则
System.out.println(this.num); // 内部类的成员变量
System.out.println(Outer.this.num); // 外部类的成员变量
}
}
// 外部类的方法
public void methodOut() {
System.out.println("外部类的方法");
new Inner().methodInner(); // 调用内部类的方法
}
}
8.引用类型用法总结
实际的开发中,引用类型的使用非常重要,也是非常普遍的。我们可以在理解基本类型的使用方式基础上,进一步去掌握引用类型的使用方式。
基本类型可以作为成员变量、作为方法的参数、作为方法的返回值,那么当然引用类型也是可以的。
8.1 class 作为成员变量
在定义一个类Role(游戏角色)时,代码如下:
public class Role {
int id; // 角色id
int blood; // 生命值
String name; // 角色名称
}
使用 int 类型表示 角色id和生命值,使用 String 类型表示姓名。此时, String 本身就是引用类型,由于使用的方式类似常量,所以往往忽略了它是引用类型的存在。如果我们继续丰富这个类的定义,给 Role 增加武器,穿戴装备等属性,我们将如何编写呢?
定义武器类,将增加攻击能力:
public class Weapon {
String name; // 武器名称
int hurt; // 伤害值
public Weapon() {
}
public Weapon(String name, int hurt) {
this.name = name;
this.hurt = hurt;
}
// ... 省略get/set方法
}
定义穿戴盔甲类,将增加防御能力,也就是提升生命值:
public class Armour {
String name; // 装备名称
int protect; // 防御值
public Armour() {
}
public Armour(String name, int protect) {
this.name = name;
this.protect = protect;
}
// ... 省略get/set方法
}
再次定义角色类:
public class Role {
int id;
int blood;
String name;
// 添加武器属性
Weapon wp;
// 添加盔甲属性
Armour ar;
// 攻击方法
public void attack(){
System.out.println("使用"+ wp.getName() +", 造成"+wp.getHurt()+"点伤害");
}
// 穿戴盔甲
public void wear(){
// 增加防御,就是增加blood值
this.blood += ar.getProtect();
System.out.println("穿上"+ar.getName()+", 生命值增加"+ar.getProtect());
}
// ... 省略get/set方法
}
定义测试类
public class DemoMain {
public static void main(String[] args) {
// 创建Weapon 对象
Weapon wp = new Weapon("屠龙宝刀" , 999999);
//创建Armour 对象
Armour ar = new Armour("麒麟甲",10000);
// 创建Role 对象
Role r = new Role();
// 设置武器属性
r.setWp(wp);
// 设置盔甲属性
r.setAr(ar);
// 攻击
r.attack();
// 穿戴盔
r.wear();
}
}
输出结果:
使用屠龙宝刀, 造成999999点伤害
穿上麒麟甲, 生命值增加10000
8.2 interface 作为成员变量
接口是对方法的封装,对应游戏当中,可以看作是扩展游戏角色的技能。所以,如果想扩展更强大技能,我们在 Role
中,可以增加接口作为成员变量,来设置不同的技能。
定义接口:
// 法术攻击
public interface FaShuSkill {
public abstract void faShuAttack();
}
定义角色类:
public class Role {
FaShuSkill fs;
public void setFaShuSkill(FaShuSkill fs) {
this.fs = fs;
}
// 法术攻击
public void faShuSkillAttack(){
System.out.print("发动法术攻击:");
fs.faShuAttack();
System.out.println("攻击完毕");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 创建游戏角色
Role role = new Role();
// 设置角色法术技能
role.setFaShuSkill(new FaShuSkill() {
@Override
public void faShuAttack() {
System.out.println("纵横天下");
}
});
// 发动法术攻击
role.faShuSkillAttack();
// 更换技能
role.setFaShuSkill(new FaShuSkill() {
@Override
public void faShuAttack() {
System.out.println("逆转乾坤");
}
});
// 发动法术攻击
role.faShuSkillAttack();
}
}
输出结果:
发动法术攻击:纵横天下
攻击完毕
发动法术攻击:逆转乾坤
攻击完毕
总结:
- 我们使用一个接口,作为成员变量,以便随时更换技能,这样的设计更为灵活,增强了程序的扩展性。
- 接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象。