Javaoop基础
对象和封装
在前面的章节中,学习了程序设计的基本知识和流程控制语句。通过这些内容的学习,大家能够用Java语言进行程序设计,但这些程序的规模都很小,一般只有几十行代码。假设要编程解决一个很大的问题,需要写几万行代码,如果按照以前的做法,将这些代码都放在一个Java文件中,可以想象这个文件会冗长,而且很难维护。所以我们学习了Java程序设计的另一道风景——面向对象程序设计,英语缩写为oop。面向对象程序设计是一个里程碑,Alan Key因为设计了世界上第一个面向对象语言Smalltalk而获得图灵奖。Java之父James Gosling结合Internet背景设计了完全面向对象的Java语言。
面向对象设计的过程
面向对象设计的过程就是抽象的过程,分以下三步来完成。
- 第一步:发现类。
- 第二步:发现类的属性。
- 第三步:发现类的方法
创建对象,调用属性和方法
类的作用就是创建对象。由类生成对象,称为类的实例化过程。一个示例也就是一个对象,一个类可以生成多个对象。创建对象语法如下:
类名 对象名 = new 类名();
在创建类的对象时,需要使用Java的new关键字。
考虑到每个对象的属性值可能是不一样的,所以在创建对象后再给它的数据成员赋值。在Java中,要引用对象的属性和方法,需要使用“ . ”操作符。其中,对象名在圆点的左边,属性或方法的名称在圆点的右边。如下:
对象名.属性 //引用对象的属性
对象名.方法名() //引用对象的方法
构造方法
public class Penguin {
String name = "无名氏";
int health = 100;
int love = 0;
String sex = "Q仔";
public Penguin() {
name = "楠楠";
love = 20;
sex = "Q妹";
System.out.println("执行构造方法");
}
public void Print() {
System.out.println("宠物的自白:\n我的名字叫"+this.name+",健康值是:"+this.health+",和主人的亲密度是:"+this.love+",性别是:"+this.sex+"。");
}
public static void main(String[] args) {
Penguin pgn = null;
pgn = new Penguin();
pgn.Print();
}
}
其中,Penguin{ )方法就是Penguin类的构造方法,从运行结果可以看到,当执行语句pgn= new Penguin();方法时,就会执行构造方法Penguin( )方法中的代码。构造方法是-个特殊的方法,当没有Penguin( )方法时,系统会提供一个空的Penguin( )方法。
构造方法的名称和类名相同,没有返回值类型。构造方法的主要作用就是在创建对象时执行一些初始化操作。如给成员属性赋初值。
重载
public class Penguin {
String name ="无名氏";
int health = 100;
int love = 0;
final String SEX_MALE="雄";
final String SEX_FEMALE="雌";
String sex =SEX_MALE;
public Penguin(){
name = "楠楠";
love = 20;
sex = "Q妹";
System.out.println("执行构造函数");
}
public Penguin(String name ,String sex) {
this.name=name;
this.sex = sex;
}
public Penguin(String name ,int health,int love,String sex) {
this.name=name;
this.sex = sex;
this.health=health;
this.love = love;
}
public void print()
{
System.out.println("宠物的自白:\n我的名字叫"+name+"健康值是"+health+"和主人的亲密度是"+love+"性别是"+sex);
}
public static void main(String[] args) {
Penguin pgn = null;
pgn = new Penguin();
pgn.print();
pgn = new Penguin("亚亚","企鹅");
pgn.print();
pgn = new Penguin("美美",80,20,"Q仔");
pgn.print();
}
}
共有三个构造函数,方法名相同参数列表不同,这称为构造方法的重载,可以通过构造方法的重载来实现多种初始化行为,在创建对象时可以根据需要选中合适的构造方法。
方法重载的判断依据如下。
- 必须在同一个类里。
- 方法名相同。
- 参数列表(方法参数的个数或参数类型)不同。
- 与方法返回值和方法修饰符没有任何关系。
在使用this调用自身的其他构造方法时,只能作为第一条语句。
static修饰符
static可以用来修饰属性、方法和代码块。static 修饰的变量属于这个类所有,即由这个类创建的所有对象共用同一个static变量。通常把static修饰的属性和方法称为类属性(类变量)和类方法。不使用static 修饰的属性和方法属于单个对象,通常称为实例属性(实例变量)和实例方法。使用static修饰方法最常见的例子是我们熟悉的main方法。下面通过一个实例来学习static的用法及使用static修饰属性和代码块。是如何分配内存空间的。
public class StaticTest {
static int i;
static int m=30;
int j;
int k = 25;
static {
i=10;
System.out.println("i的初始值"+i);
}
public StaticTest(){
j=20;
System.out.println("j的初始值"+i);
}
public static void getNum()
{
System.out.println("得到i的值为:"+i);
}
public static void main(String[] args) {
StaticTest st = new StaticTest();
System.out.println("i的值为"+StaticTest.i);
st.getNum();
System.out.println(st.m);
System.out.println(st.k);
}
}
从示例中可以得出以下结论。
- 在加载类的过程中, 完成静态变量的内存分配,再执行静态块,两者是在创建对象之前执行的。
- 类属性和类方法可以通过类 名和对象名访问,实例属性和实例方法只能通过对象名访问。
- 类方法只能访问类属性和其他类方法。
注意:静态方法中不能使用this和super关键字。super关键字在后面的章节会详细说到
封装
public class Dog {
public String name ="无名氏";
public int health = 100;
public int love = 0;
public String strain ="拉布拉多";
public Dog() {
}
public Dog(String name ,String strain) {
this.name = name;
this.strain = strain;
}
public void eat() {
if (health >=100) {
System.out.println("狗狗需要多运动");
}else {
health = health +3;
System.out.println("狗狗吃饱了");
}
}
public void play() {
if (health <60) {
System.out.println("狗狗生病了");
}else {
System.out.println("狗狗正在玩耍");
health = health -10;
love = love+5;
}
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getHealth()
{
return health;
}
public void setHealth(int health)
{
if (health > 100 || health <0) {
this.health=40;
System.out.println("健康值应该在0到100中默认值40");
}
else {
this.health=health;
}
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love=love;
}
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain=strain;
}
public void print()
{
System.out.println("宠物的自白:\n我的名字叫"+name+"健康值是"+health+"和主人的亲密度是"+love+"我是一只"+strain);
}
}
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog("ouou","kukude");
dog.play();
System.out.println("健康只是"+dog.getHealth());
dog.eat();
dog.print();
}
}
封装是面向对象的三大特性之一,就是将类的状态信息隐藏在类内部,不允许外部程序直接访问,而通过给雷提供的方法来实现对隐藏信息的操作和访问。
封装的具体步骤:修改属性的可见性来限制对属性的访问:为每个属性创建一对赋值 (setter)方法和取值(getter) 方法,用于对这些属性的存取:在赋值方法中,加入对属性的存取控制语句。
封装的好处:隐藏类的实现细节,让使用者只能通过程序规定的方法来访问数据,可以方便地加入存取控制语句,限制不合理操作。
封装时会用到多个权限控制符来修饰成员变量和方法,区别如下。
- private: 成员变量和方法只能在其定义的类中被访问,具有类可见性。
- 默认:成员变量和方法只能被同一个包里的类访问,具有包可见性。
- protected: 可以被同一个包中的类访问,被同一个项目中不同包中的子类访问。
- public:可以被同一个项目中的所有类访问,具有项目可见性,这是最大的访问权限。
this关键字
- 使用this调用成员变量,解决成员变量和局部变量的同名冲突。
- 使用this调用成员的方法。
- 使用this调用重载的构造方法,只能在构造方法中使用,必须时构造方法的第一条语句。
继承
人类是一个类但不过分很多的工作很显然从图可以看出两个类有完全相同的属性:年龄(Age),性别(Gender),编号(ID)和姓名(Name),也就是说,两个类中描述这些相同属性的代码也是相同的。如果扩展这个程序,加入CEO,CFO之类的角色,他们必然也有年龄,性别,编号,和姓名这些属性,编码是因为岗位不同会编写大量关于这些属性的重复代码,造成冗余。随着系统规模的扩大,冗余越来越多,从商业开发的角度考虑,这样冗余的代码是不可以容忍的。
如何避免这种冗余,把冗余代码集中起来重复使用呢? 那么就需要用类的三大特性之一继承:子类继承父类
1、继承
继承的语法:
访问修饰符 SubClass extends SuperClass {
}
继承通过关键字来实现,其中的SubClass称为子类,SuperClass 称为父类、基类或超类。修饰符如果是public, 则该类在整个项目中可见: 若无public修饰符,则该类只在当包可见;不可以使用private和protected修饰类.
在Java中,子类可以从父类中集成到拿些“财产”呢?
- 继承 public和protected修饰的属性和方法,无论子类和父类是否在同一个包里。
- 继承默认权限修 饰符修饰的属性和方法,但子类和父类必须在同一个包里。
- 无法继承private修饰的属性和方法。
- 无法继承父类的构造方法。
访问修饰符 | 本类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
重写和继承关系中的构造方法
在子类中可以根据需求对从父类继承的方法进行重新编写,称为方法的重写或方法的覆盖,方法重写必须满足如下要求。
- 重写方法和被重写方法必须具有相同的方法名。
- 重写方法和被重写方法必须具有相同的参数列表。
- 重写方法的返回值类型必须和被重写方法的返回值类型相同或是其子类。
- 重写方法不能缩小被重写方法的访问权限。
super关键字
**super代表当前对象的直接父类对象和默认引用。**在子类中可以通过super关键字来访问父类的成员
- super必须出现在子类(子类的方法和构造方法)中,而不是其他位置。
- 可以访问父类的成员,如父类的属性,方法,构造函数
- 注意访问权限的限制,如无法通过super访问private成员
- super.name;//访问父类的name属性
- super.print();//访问直接父类的print()方法
- super(name);//访问直接父类对应的构造函数,只能出现在构造方法中
继承条件下构造方法的调用规则如下。
- 如果子类的构造方法中没有通过super显式调用父类的有参构造方法.也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,有没有"super();" 语句,效果都是一样的。
- 如果子类的构造方法中通过super显式调用父类的有参构造方法,则将执行父类相应的构造方法,而不执行父类无参构造方法。
- 如果子类的构造方法中通过this 显式调用自身的其他构造方法,则在相应构造方法中应用以上两条规则。
- 特别注意的是,如果存在多级继承关系, 则在创建一一个子类对象时, 以上规则会多次向更高一级父类应用,-直到执行顶级父类Object类的无参构造方法为止。
抽象类和抽象方法
如果继承抽象父类那么必须重写父类的方法(父类抽象方法没有方法体)除非子类也是抽象类
抽象类语法:
public abstract SuperClass{
}
抽象方法语法:
public abstract play();
抽象类和抽象方法的特点如下。
- 抽象类不能实例化。抽象类中可以没有、有一个或多个抽象方法,甚至全部方法都可以是抽象方法。
- 抽象方法只有方法声明,没有方法实现。有抽象方法的类必须声明为抽象类。子类必须重写所有的抽象方法才能实例化,则子类还是一个抽象类。
- 抽象类可以包含实例方法,也可以有抽象方法。
final关键字
- 用final修饰的类,不能再被继承。
- 用final修饰的方法,不能被子类重写。
- 用final修饰的变量(包括成员变量和局部变量)将变成常量,只能赋值一次
多态
前提两个类必须是继承关系
实现多态有两种方法:
第一种是重载,第二种是重写
第一种重载我就不再多说了上面已经详细介绍了重写的话上面也详细介绍了抽象方法
重写的特点是:不同的类,方法名,参数个数和类型相同
现在我给大家介绍第二种重写:
普通的重写:相同的方法名,参数个数和类型,注释可写可省略(@override)(子类不一定要实现父类–》没有强制)
抽象方法特点
- 必须位于抽象类中
- 子类必须重写抽象方法,除非子类也是抽象
- 没有方法体
抽象类特点:
- 抽象类不能被实例化的
- 可以有普通方法也可以有抽象方法
字符转换
- 向上转型:子类到父类
- 向下转型:父类到子类
关键字instanceof
1、判断一个变量的真实数据类型:instanceof 如果是返回true分则返回false
2、java中,类/对象三种关系:
- is a—>继承关系–》继承
- has a—>包含关系—》属性
- like a—>像-------》接口
在C#
- is:判断数据类型
- as:强转
接口
1、语法:interface
public interface 接口名{}
2、特点:
- 不能被实例化
- 成员方法:只能是抽象方法,访问修饰abstract默认访问修饰:public abstract
- 成员变量:只能定义静态常量,默认访问符:public static final
- 构造方法:不能有构造函数
3、作用:
约束子类必须拥有某种行为规定了子类一定拥有某个功能)
怎么让子类实现接口:
implements关键字语法:
public 子类 implements 接口名,接口名。。。
注意:extends在前,implements在后
接口成员:
- 不能有构造函数
- 方法:抽象方法–》public abstract
- 变量:必须有访问public static final
接口和抽象类的区别:
相同点:
- 不能new
- 约定子类必须实现方法
不同点:
- 关键字不同:抽象类:abstract;接口:interface
- 方法不一样: 抽象类可以有非抽象方法
- 构造函数:抽象类可以有构造函数;接口不能有构造函数
- 成员变量:抽象类可以有非静态的,可以不是常量 ; 接口–》static final