面向对象入门
@Author:云都小生
span {color: red;}
p {style=”font-size:22}
类与对象
现实生活中有各式各样的对象,有狗有猫有人,有树有花有草,我们所见到的东西,都是对象。这些对象有一些,拥有相同的属性和方法。例如说狗,它们同样有四条腿,两个眼睛,它们同样都会叫 Java是面向对象的编程语言,在Java中怎么实现面向对象编程呢?
public class Test
{
public static void main(String[] args)
{
Dog dog = new Dog;
}
}
//不同的类文件
public class Dog {
String color;
String name;
String sex;
Dog(String color,String name,String sex){ //构造方法
this.color = color;
this.name = name;
this.sex = sex;
}
void run()
{
System.out.print("Dog in running···\n");
}
void bray()
{
System.out.print("汪汪汪\n");
}
}
在这个例子中,Dog就是一个类,在Dog里面有这么几个属性:color、name、sex,还有三个方法。一个是Dog的构造方法,另外两个方法是run和bray。
类与对象有什么关系,有什么区别?类其实就是一个模版,对象是根据这个模版创建出来的实例。对象创建之后,自动拥有类的所有属性和方法。类并不是实际存在的,对象才是在内存中实际存在的。new这个关键字,就是用来分配内存空间。一句话理解,类就是“人”,而对象是具体的“什么样的人”。
什么是构造方法?每个类都会有构造方法,我们可以通过构造方法,一开始就给对象里面的属性赋值。如果你没有构造方法,默认的就会有一个构造方法,就像这样 Dog(){} 构造方法可以被重载。可以看下面重载的相关知识点。
Dog(String name){}
this是一个关键字,这个this算是一个指针,指向对象本身。通过构造方法传进来的那
我们可以通过对象.属性的方式,来索引对象内部的属性。Dog.name 或者 Dog.sex
对象与对象变量这里还需要谈一个小细节,对象是在内存里面实实在在存在的,而对象变量,只是一个变量,指向内存中具体对象的数据。
引用类型的内存分析
之前我们经常用String类型,其实String是一个引用类型,什么意思呢?有过指针知识基础的人应该知道,如果你不知道,莫慌。String变量其实存储的并不是真正的字符串,而是字符串在内存中存在的地址。
程序占用内存的是有不同类型的:是堆、栈、全局区、常量区、程序代码区;
1. 堆一般由程序员分配;
2. 栈一般由编译器自动分配,存放参数值,局部变量的值等;
3. 全局变量和静态变量存放的地方;
4. 常量字符串存放的地方;
5. 程序代码区。
String str = “Cloudking”; 这个str其实是存放在栈,而字符串是存放再堆里面,str会存储字符串在内存常量区的地址,通过这个地址找到相应的数据。
而在类与对象中,也是这样。对象名存储的是整个对象数据在堆中的首地址,通过这个地址去索引相应的数据。Dog dog = new Dog(); dog会指向对象数据在堆中的地址。
面向对象基本特性——重载
同一个类里面,多个方法可以有相同的名字,只要它们的参数列表不同就可以,这种方式叫“方法重载”。
public class Dog {
void printTest()
{
System.out.println("没有参数");
}
void printTest(String name)
{
System.out.println(name);
}
void printTest(int id,String name)
{
System.out.print(id + "\n" + name);
}
}
#main方法里面
Dog dog = new Dog();
dog.printTest("Cloudking");
dog.printTest(123,"Cloudking");
编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法。还有几点需要注意:
· 构造方法也可以重载;
· 方法名称必须相同,才能够重载;
· 重载的方法除了方法名外,参数不能一模一样;
· 不能用返回值来进行重载。
面向对象基本特性——静态域与静态方法
静态域又称为类变量,所有的对象,都共享这一个类变量,修饰类变量的方法,就是用static,看实例。
public class Dog {
private static int id;
}
这样一来,所有由Dog new出来的对象,都指向这同一个变量id。静态方法,就是不能对对象进行操作的方法。一般用到静态方法,都有两点:
- 方法不需要访问对象状态;
-
- 只需要访问类的静态域。
public class Dog {
private static double x = 8.888;private static void Test() { System.out.println(getX()); } public static double getX() { return x; } public static void setX(double x) { Dog.x = x; }
}
//main
Dog dog1 = new Dog();
Dog dog2 = new Dog();
dog1.setX(12);
System.out.println(Dog.getX()); - 只需要访问类的静态域。
还有一个小插曲,关于静态常量,我们用final来修饰常量,用static来修饰它是一个静态的常量。
public class Math{
...
public static final double PI = 3.14159265358979323846;
...
}
本来加上final就修饰成常量了,再加上static,就不用每次都需要new一个对象才能使用这个常量。
面向对象特性——封装
面向对象的第一大特性——封装。这个词我们并不陌生,在面向对象中,封装指的是将细节包装、隐藏起来,防止该类的代码和数据被外部类定义的代码随机访问。这样一来,可以使代码变得安全(可靠)、更容易维护(重用性)。
以下把优点列出来:
1. 良好的封装能够减少耦合;<br/>
2. 类内部的结构可以自由修改;<br/>
3. 可以对成员变量进行更精确的控制;<br/>
4. 隐藏信息,实现细节。<br/>
怎么对类中的属性进行封装呢?把类的属性设置为私有,对外提供“构造器”和“访问器”。
public class Dog {
private String color;
private String name;
private String sex;
Dog(String color,String name,String sex){ //构造方法
this.color = color;
this.name = name;
this.sex = sex;
}
void run()
{
System.out.print("Dog in running···\n");
}
void bray()
{
System.out.print("汪汪汪\n");
}
}
把类中的属性,都加上private这个权限修饰符,就会变成类私有的属性。如果你在该类的外部去访问、修改,就会出现错误。这样一来,我们就实现了属性隐藏,那怎么才能间接性的访问、修改它们。这里就涉及到了访问器和修改器,它们能让我们间接的访问、修改类中隐藏的属性。
public class Dog {
private int id;
private String name;
Dog(int id,String name)
{
this.id = id;
this.name = name;
}
public String getname()
{
return this.name;
}
public int getid()
{
return this.id;
}
public void setname(String name)
{
this.name = name;
}
public void setid(int id)
{
this.id = id;
}
}
看看这个类,把内部的属性用private隐藏了起来,通过private修饰的属性和方法,都只能在类内部被访问、修改,不能被外部直接访问(get)、修改(set)。这样做有什么好处呢?第一点,安全性,第二点,屏蔽细节。
如果用户在设置数值的时候随意输,你要怎么办?你要在哪里检查?我们来看一个set方法。
public void setid(int id)
{
if( id <= 100 && id >= 1)
{
this.id = id;
}
else
{
System.out.println("输入有误!");
}
}
类内部的方法也可以被private修饰,被private修饰的方法,只能在类内部被使用。
面向对象特性——继承
动物界中存在这么一种情况,儿子往往会继承父母的优点,就像我们,我们有可能就继承了自己父母的许多特性。而在面向对象编程中,也有这么一个特性——继承。在Java中,类与类之间可以有继承的关系,子类可以继承父类的属性和方法。
public class Animal {
String color;
void bray()
{
System.out.println("bray~");
}
}
public class eatgrassAnimal extends Animal {
void eat()
{
System.out.println("eat grass");
}
}
#main
eatgrassAnimal d = new eatgrassAnimal();
d.color = "yellow";
d.bray();
d.eat();
eatgrassAnimal从Animal类里继承,自动拥有了Animal类非private的属性和方法,它也可以拥有自己的属性和方法,作为对父类的扩展,继承可以增加程序的复用性。extends关键字用来修饰一个类从另一类中继承,Java中所有的类默认都从Object类继承。
java里只支持单继承,一个子类只能有一个父类,但是可以进行继承,简答的说,A可以从B继承,B可以从C继承,一代更比一代强。子类中如果想访问父类里面的对象,可以使用super的关键字。
public class Animal {
String color;
Animal(String color)
{
this.color = color;
}
void bray()
{
System.out.println("bray~");
}
}
public class eatgrassAnimal extends Animal
{
String name;
eatgrassAnimal(String color,String name) {
super(color);
this.name = name;
}
void eat()
{
System.out.println("eat grass");
}
void bray()
{
super.bray();
System.out.println("eatgrassAnimal bray~");
}
}
#main
eatgrassAnimal e = new eatgrassAnimal("yellow","羊");
e.bray();
super可以调用父类的构造方法、调用父类的方法(当子类覆盖父类的方法时)、访问父类的数据域。
每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。子类覆盖或重写了父类的方法,则只有使用 super 才能在子类中调用父类中的被重写的方法。
从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
面向对象特性——多态
由于有了继承,就延伸出了另一种特性——多态。多个子类都从同一个父类继承,像食草动物、食肉动物,都是继承自动物。在我们在编写程序的过程中,并不知道什么时候会传过来食草动物的对象、什么时候会传过来食肉动物的对象,所以我们在调用方法的时候,总不能直接就对单一的对象进行处理吧。
假设食草动物、食肉动物都有同一个方法——run(),但是在我们在调用它们这个方法的时候,需要传入不同的对象,根据不同的对象去调用它们各自的run()方法。这个就是动态绑定,根据传入的对象,去调用属于它们各自的方法。
public class eatgrassAnimal extends Animal
{
void eat()
{
System.out.println("eat grass");
}
}
public class eatmeatAnimal extends Animal {
void eat()
{
System.out.println("eat meat");
}
}
public class Animal {
void eat()
{
System.out.println("eat···");
}
}
public class Test
{
public static void main(String[] args)
{
eatgrassAnimal g = new eatgrassAnimal();
eatmeatAnimal m = new eatmeatAnimal();
print(g);
print(m);
}
static void print(Animal a)
{
a.eat();
}
}
可以看到两个类eatgrassAnimal和eatmeatAnimal都是从Animal继承,都重写了Animal的eat()方法。当我们想根据传入的对象,调用它们具体的方法时,就可以写成pritn(Animal),父类引用指向子类对象。
多态的实现必须有三个条件:
1. 继承
2. 方法重写
3. 父类引用指向子类对象
现实中其实有很多多态的例子,我们能在word文档里面使用的快捷键,换到了桌口下面,也能使用快捷键,只是做出的反应不一样。
在游戏设计中,也会有多态设计的例子,例如说坦克和飞机,都是攻击性的机器,它们都有这样一个方法attack(攻击),但是它们攻击的方式都不一样,也就是方法里面实现的不一样。这样,调用attack()方法,我们就可以利用多态来实现。
public class Machine {
void attack()
{
System.out.println("Attack中···");
}
}
public class Aircraft extends Machine {
void attack()
{
System.out.println("战机导弹发射!");
}
}
public class Tanke extends Machine {
void attack()
{
System.out.println("坦克炮弹发射!");
}
}
public class Test
{
public static void main(String[] args)
{
Tanke t = new Tanke();
Aircraft a = new Aircraft();
Attack(t);
Attack(a);
}
static void Attack(Machine m)
{
m.attack();
}
}
面向对象特性——抽象类
抽象类和一般的类没有太大的区别,只是多了个抽象方法。类中有一部分不能确定——既需要这种功能,又没办法去定义主题,就可以写成抽象类。
抽象方法和抽象类都必须被abstract关键字修饰。抽象类中的抽象方法要被使用,必须由子类重写所有的抽象方法后才能调用。记住,是重写,不是纯粹的把代码复制,那样依旧是一个抽象类。
abstract class Test {
abstract void test();
}
这个Test抽象类,可以实现其他的很多方法,但是这里面只要有一个抽象方法,就必须被修饰成抽象类。并且,这类还不能被final修饰,抽象类就是要继承,然后让子类去实现抽象方法,其次,方法也不能被static和private修饰。static关键字会把方法修饰成公用,即使没有对象也能直接用,那样就没有意义了,如果是private,抽象方法就不能被子类继承了。
归根结底,当一个类里面有一些不被确定的方法时,就可以使用abstact来修饰,让它成为抽象类,然后让子类去实现这个抽象方法。
面向对象特性——接口
接口不是类,而是对类的一组需求描述。Java没有提供多继承的机制,没有办法同时继承两个类,但是Java却可以实现多个接口。一个接口只有方法的特征,而没有方法的实现,因此这些方法在不同的地方被实现时,可以具有完全不同的行为。(是不是感觉跟抽象类很像)
在Java中使用interface来定义一个接口,而使用implements来实现接口。
public interface Test{
public static final int num; //成员常量具有固定的修饰符:public static final
public abstract void method; //成员函数具有固定的修饰符:public abstract
}
public class Testimpl implements Test{
// 实现接口中的所有方法
.....
}
接口的确跟抽象类很像,但是它本质上并不是类,而且,接口与接口之间可以是继承关系,而且可以实现多继承。接口比抽象类更抽象。
2017/9/2 22:52:38 @Author:云都小生