02 JAVA 对象

来源:互联网 发布:微信开发团队 知乎 编辑:程序博客网 时间:2024/06/05 12:38

一天可以new无数个对象,可是老妈还在让我找对象。。。


面向对象的三个基本特点:1. 封装性 2. 继承 3. 多态

易于扩展和维护,封装性好,复用性高

一、面向过程 VS 面向对象

(就喜欢紫色)

面向过程完成事情的步骤,我们一个一个方法实现步骤,然后依次调用,而面向对象是使用某些对象

面向对象优点:

- 我们不需要知道某些方法是如何构建的,封装性好

- 易于管理

面向对象缺点:执行效率

例1:做菜

- 过程

1. 洗菜 2. 放油热锅 3. 炒菜

用户是个厨师,知道所有的步骤

- 对象

厨师对象,有做菜的动态属性

用户是个食客,通知厨师做菜即可

例2:某个游戏的过程(奇葩的游戏,每次玩家执行动作之前都跳一下~不要问我是什么游戏)

玩家1输入

jump()

action(参数)

玩家2输入

jump()

action(参数)

...n个玩家...

判断

返回第一步

此时,我们想要给某个jump()方法添加一个参数(这里不关心参数哪里来的)

- 过程

想给jump()加入一个参数height,此时程序里面所有用到该jump()方法的地方都要修改

- 对象

后台对象b

玩家1输入

b.implement(参数)

玩家2输入

b.implement(参数)

...n个玩家...

判断

返回第一步

1. 技能类Skill,我们需要修改相关的动态属性jump()设定跳高度限制->jump(参数1)

2. 对于后台类

Skill s = new Skill

implement(参数){

获得输入

s.jump()-》s.jump(参数1)

s.action(参数)

}

例3:一个人可以养一只宠物,主人一逗它,它就叫

- 过程

主人逗它,判断宠物类型,宠物叫,但是不同的主人可能养了不同的宠物,猫是喵喵叫,狗是汪汪叫,小鸟是唧唧喳喳叫......我们很难一下子把所有的宠物可能都列出来(对于面向过程,我们需要添加方法,然后修改逻辑判断)

- 对象

动物对象

人对象

人调用动物的叫方法,如果是猫,我们可以创建猫子类,并重写自己的叫方法,不需要修改其他地方的代码

二、对象

对象利用计算机语言对问题域中事务进行描述,类用来描述对象的一个抽象的概念,类可以看作是对象的模板,对象可以看成该类的一个具体实例

- 属性(静态属性)

- 方法(动态属性)


三、类之间的关系

- 关联

这是最弱的一种关系(一个类中的方法参数是另一个类),比如”使用“,人使用车


- 继承

子类拥有父类的属性,什么是什么的一种,JAVA只允许单继承(每一个类只能有一个父类,但一个父类可以有多个子类),多继承可以依靠接口实现


- 聚合

组合 VS 聚集

聚合表示了整体和部分的关系,而根据这种关系的松紧耦合又分为了聚集和组合,组合的每一个部分都是必不可少的,就像人不能没有大脑,而鸟群可以少了一只鸟

- 实现

实现接口,不同的对象对某个方法有不同的实现方式


- 多态



对象都有对外服务的接口,通过继承可以复用,对象隐藏内部服务的实现通过聚合可以复用。组件时比对象更高层次上的复用,相当于好多对象,过程的组合

四、类的定义

【修饰符】class 类名{

成员变量

方法

}

修饰符: public, protected, private, 缺省

成员变量(注意它与局部变量有初始化的区别),它的作用域是整个类,注意类中非static成员变量或者方法不能用在static的方法中,因为static的类成员或者方法是针对所有该类所有对象的,而非static的类成员或方法是针对某个对象的。只有实例化类时,非静态的成员才被初始化,但是我们可以直接调用静态方法,此时如果方法内有非静态成员变量就会出错~

http://blog.sina.com.cn/s/blog_411fed0c0102vh5x.html

静态方法的内存空间是在加载到内存后就被分配到的,而不是等到调用的时候才给分配。静态方法被加载到了内存,分配了空间,无论你是否使用,它就在那里,有且只有一个就它自己。无论多少个线程调用,都是调用的它自己就那么一个。不同通过类的对象进行调用的方法,那种方法初始化类的对象的时候才被分配空间。而且java有垃圾回收机制,跑出作用域之后它所占的内存就会被回收。

调用非static方法

static void m(Object obj) {

obj.nonStaticM();

}


五、引用

引用类型一般占用两字节(虚拟的物理地址)


code seg: 类,方法

data seg: 静态数据和方法,字符串

stack: 执行过程中的临时变量

heap: 实例,不同的实例可能是不同的大小,因为成员变量值可能是不同的,所以不能实现分配大小

六、构造方法

- 初始化对象的函数,new创建实例的时候调用

- 名字和类同名

- 没有返回值

- 如果没有构造方法,编译器自动添加:

类名(){}

- 但是一旦一个类定义了一个构造方法,系统就不会自动添加

七、命名规则

- 类名首字母要大写,文件名与public类名相同

- 变量名和方法名要小写

- 运用驼峰标识,如furColor

八、内存解析

注意一个内存块数据,没有任何引用指向它的时候的时候就会被自动回收

class BirthDate {    private int day;    private int month;    private int year;        public BirthDate(int d, int m, int y) {        day = d;         month = m;         year = y;    }        public void setDay(int d) {    day = d;  }      public void setMonth(int m) {    month = m;    }        public void setYear(int y) {    year = y;    }        public int getDay() {    return day;    }        public int getMonth() {    return month;    }        public int getYear() {    return year;    }        public void display() {    System.out.println        (day + " - " + month + " - " + year);    }}public class Test{    public static void main(String args[]){        Test test = new Test();        int date = 9;        BirthDate d1= new BirthDate(7,7,1970);        BirthDate d2= new BirthDate(1,1,2000);            test.change1(date);        test.change2(d1);        test.change3(d2);        System.out.println("date=" + date);        d1.display();        d2.display();    }        public void change1(int i){    i = 1234;    }        public void change2(BirthDate b) {    b = new BirthDate(22,2,2004);    }        public void change3(BirthDate b) {    b.setDay(22);    }}

一个内存块,除了this对自身的引用外,再无其他的引用指向它时~图中x表示临时的~


class Point {    private double x;    private double y;    Point(double x1, double y1) {     x = x1;     y = y1;    }    public double getX() { return x; }    public double getY() { return y; }    public void setX(double i) { x = i; }    public void setY(double i) { y = i; }}class Circle {    private Point o;    private double radius;    Circle(Point p, double r) {    o = p;     radius = r;    }    Circle(double r) {        o = new Point(0.0, 0.0);        radius = r;    }        boolean contains(Point p) {    double x = p.getX() - o.getX();    double y = p.getY() - o.getY();    if(x*x + y*y > radius * radius) return false;    else return true;    }            public void setO(double x, double y) {        o.setX(x);         o.setY(y);    }    public Point getO() { return o; }    public double getRadius() { return radius;}    public void setRadius(double r) { radius = r;}    public double area() {         return 3.14 * radius * radius;    }}public class TestCircle {    public static void main(String args[]) {        Circle c1 = new Circle(new Point(1.0,2.0), 2.0);        Circle c2 = new Circle(5.0);        System.out.println("c1:("+c1.getO().getX()+","            +c1.getO().getY()+"),"+c1.getRadius());        System.out.println("c2:("+c2.getO().getX()            +","+c2.getO().getY()+"),"+c2.getRadius());        System.out.println("c1 area = "+c1.area());        System.out.println("c1 area = "+c2.area());        c1.setO(5,6);        c2.setRadius(9.0);        System.out.println("c1:("+c1.getO().getX()+","            +c1.getO().getY()+"),"+c1.getRadius());        System.out.println("c2:("+c2.getO().getX()+","            +c2.getO().getY()+"),"+c2.getRadius());        System.out.println("c1 area = "+c1.area());        System.out.println("c1 area = "+c2.area());                Point p1 = new Point(5.2, 6.3);        System.out.println(c1.contains(p1));        System.out.println(c1.contains(new Point(10.0,9.0)));            }}


九、方法重载overload

我们可以在一个类中定义相同名字的方法(但是参数是不同的,比如数据类型,参数个数),调用时,会根据不同的参数表选择对应的方法,构造方法也可以被重载

十、关键字this

this指向使用当前方法的对象,this可以被看做一个变量,是当前对象的引用,每个对象中都存在一个this


public class Leaf {int i = 1;Leaf(int i) {this.i = i;}Leaf increament() {i++;return this;}void print() {System.out.println("i="+i);}public static void main(String args[]){Leaf leaf = new Leaf(100);leaf.increament().increament().print();}}
结果是:

i=102



十一、关键字static

static类型的数据,不需要创建实例就已经在内存里面了

static方法和数据都在数据区内

- 静态成员变量(存放在数据区),为该类的公共变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份

- 静态方法,不针对某个对象调用,所以在static方法中不可访问非static成员,可以通过对象引用或者类名访问静态成员

http://blog.csdn.net/peterwin1987/article/details/7571808

static方法就是类装载的时候就加载进来了,而非static方法是调用的时候才装载到内存

http://zhidao.baidu.com/question/1817880738458676908.html

public class Cat {    private static int sid = 0;    private String name;     int id;    Cat(String name) {        this.name = name;          id = sid++;    }    public void info(){        System.out.println               ("My name is "+name+" No."+id);    }    public static void main(String arg[]){        Cat.sid = 100;        Cat mimi = new Cat("mimi");        mimi.sid = 2000;        Cat pipi = new Cat("pipi");        mimi.info();         pipi.info();    }}


十二、package和import

package: 提供类的多重命名空间,不同的包可以含有相同名字的类,包路径规则通常是把公司域名倒过来比如net.princeton.包名,此时我们需要将net的上一层目录放入classpath中。如果程序没有package语句,则默认指定为无名包


- 编译出来的class文件必须位于正确的目录下面,并在classpath中添加该目录

- 如果想要在另一个类用这个包里的类,必须把名字写全,或者import(".",包)

- 不要把源代码放到放class的包中,可能会出错

Eclipse为不同的项目设置不同的classpath,不共享class文件


一些主要的包:

目录jdk/jre/lib/rt.jar/java

java.lang 包含一些JAVA的核心类,如String, Math, Integer, System, Thread等,默认引入

java.awt 与界面GUI相关的类

java.applet applet运行所需的一些类

java.net 网络相关的操作的类

java.io 提供多种输入、输出功能的类

java.util 一些实用的工具类,如定义系统特性,使用与日期日历相关的函数

classpath里面的jar包目录包含了jar包的名字

jar -cvf xx.jar *.* 解压压缩包

十三、继承和权限

关键字extends

子类拥有基类所有的成员变量和方法,只支持单继承,一个子类只能有一个基类,但一个基类可以有多个子类

修饰符有4个权限:public, private, protected, 缺省

class只能缺省或被public修饰


对于default和protected来说,其实应该这样讲,不同包中的类不能继承default类,而且不同包中的子类不能访问父类的default成员

十四、重写override

对父类方法进行重写(必须有相同的名称,参数和返回类型),重写方法不能使用比被重写的方法采用更严格的权限,里氏替换原则,子类对象可以被当成父类对象使用

十五、关键字super

使用super来引用父类的this

对于子类的构造方法,必须使用父类的构造方法,换言之,我们创建子类的同时创建了父类,我们可以使用super来实现,this可以用来调用本类的另外构造方法。如果子类没有调用基类的构造方法,系统默认调用基类的无参数构造方法,但是如果父类没有无参构造函数,则出错。super都写在第一行

多看例子就懂了吧~

class Parent {public int value;public void f() {value = 100;System.out.println("Parent: "+value);}}class Child extends Parent {public int value;public void f() {super.f();value = 200;System.out.println("Child: "+value);System.out.println("parent: "+super.value);}}public class TestSuper {public static void main(String args[]) {Child c = new Child();c.f();}}

结果:

Parent: 100

Child: 200

parent: 100


class A {A() {print("A()");}protected void print(String a) {System.out.println(a);}public void f() {print("A:f()");}}class B extends A {B() {print("B()");}public void f() {print("B:f()");}}public class TestSuper2 {public static void main(String[] args) {B b = new B();b.f();}}

结果:

A()

B()

B:f()

class Person1 {private String name;private String location;Person1(String name) {this.name = name;location = "Beijing" ;}Person1(String name, String location) {this.name = name;this.location = location;}public String info() {return "name: "+name+" location: "+location;}}class Student1 extends Person1 {private String school;Student1(String name, String school) {this(name, "beijing", school);}Student1(String name, String location, String school) {super(name, location);this.school = school;}public String info() {return super.info()+" school: "+school;}}public class TestSuper3 {public static void main(String[] args) {Student1 sd =new Student1("Mike", "IC");System.out.println(sd.info());}}
结果:

name: Mike location: beijing school: IC

十六、大基类Object

Object是所有类的基类,当我们没写extends的时候,就默认继承Object

属性Class, 方法clone,getClass,toString,hashcode,equals,finalize等

十七、hashcode

java hash好白痴的,因为可能会发生两个不同内容对应同一个key的可能

十八、toString

Object类定义了一个public String toString()方法,返回String类型,描述当前对象有关信息,当String与其他类型进行数据连接时会自动调用这个函数,如System.out.println("info"+person),可以根据需要在子类中重写

toString(对象)的结果是类名@hash值

十九、equals方法

Object类还提供了public boolean equals(Object obj)方法,判断两个对象是否相等,默认是根据引用对象是否指向同一个来判断(与==作用相同),但是我们可以重写来根据对象的内容,而不根据引用来判断:

if(obj==null) return false;

else{

if(obj instanceof Cat){

Cat c=(Cat)obj;

if(c.color==this.color……){

return true;

}

}

}

return false;

public class TestEqual {public static void main(String[] args) {Integer i1 = new Integer(1);Integer i2 = new Integer(1);System.out.println(i1 == i2);System.out.println(i1.equals(i2));Mao m1 = new Mao("A", "A");Mao m2 = new Mao("A", "A");System.out.println(m1 == m2);System.out.println(m1.equals(m2));}}class Mao {  String name;  String color;  Mao(String n,String c){    name = n; color = c;  }  

结果:

false

true

false

true

二十、对象转型casting
一个基类的引用类型变量可以指向其子类的对象,一个基类的引用不可以访问其子类对象新增加的成员,也就是比如别人把狗当成动物传给你,你只能把他当做动物,我们可以使用“变量 instanceof 类名”来判断该引用型变量所指向的对象是否属于该类或该类的子类
- upcasting 将子类对象当基类对象使用
- downcasting 将基类当子类对象使用,例如cat(obj)
class Animal1 {public String name;Animal1(String name) {this.name =  name;}}class Cat1 extends Animal1{public String color;Cat1(String name, String color) {super(name);this.color = color;}void f(){System.out.println("喵喵喵~");}}public class TestCasting {public static void main(String args[]) {String name = "Shirley", color = "Brown";String name1 = "Bobby", color1 = "Black";Cat1 c1 = new Cat1(name,color);Animal1 c2 = new Cat1(name1,color1); //upcastingSystem.out.println("cat1, name: "+c1.name+" color: "+c1.color);c1.f();//System.out.print("cat2, name: "+c2.name+" color: "+c2.color);不能执行,因为父类不能调用子类的成员变量color//c2.f();不能执行,因为是Animal1引用类型变量,不能调用子类的成员//downcastingif (c2 instanceof Cat1) {Cat1 c2_2 = (Cat1) c2;System.out.println("cat2, name: "+c2_2.name+" color: "+c2_2.color);c2_2.f();}}}




二十二、动态绑定

动态绑定(多态,池绑定)

执行期间绑定,根据实际类调用相应的方法,而不是编译期间绑定,实现条件:

- 继承

- 重写

- 父类引用指向子类对象

class Animal2 {public void enjoy() {System.out.println("叫");}}class Person2 {String name;Animal2 a2;Person2(Animal2 a2, String name) {this.a2 = a2;this.name = name;}void petEnjoy(){a2.enjoy();}}class Bird extends Animal2 {public void enjoy() {System.out.println("唧唧喳喳");}}class Dog1 extends Animal2 {public void enjoy() {System.out.println("汪汪汪");}}public class TestPool {public static void main(String args[]) {Animal2 b = new Bird();Animal2 d = new Dog1();Person2 p1 =  new Person2(b, "Amy");Person2 p2 =  new Person2(d, "Jack");System.out.print(p1.name+"'s pet ");p1.petEnjoy();System.out.print(p2.name+"'s pet ");p2.petEnjoy();}}

结果:

Amy's pet 唧唧喳喳

Jack's pet 汪汪汪

前面学过,父类不能调用子类的方法,但是看下多态,当子类中有相同名字和参数的方法时,就调用子类的


二十三、抽象类

关键字abstract

看下动态绑定的例子,有没有发现其实那个Animal的enjoy方法根本不需要实现,此时抽象方法就被引入了,抽象方法的拥有类必须为抽象类(但是抽象类除了抽象方法还可以拥有普通的成员变量和普通的方法),抽象方法只需要被声明而不实现,所以抽象类必须被继承,方法必须被重写,抽象类不能实例化,抽象可以继承抽象,注意抽象类依然保持单继承性。

二十四、关键字final

final变量的值不能被改变,方法不能被重写,类不能被继承,这样可以保护数据。方法参数如果是final类型的话,这个参数在方法中不能被改变,如果参数是个引用,则该引用不能指向别的对象,但是其实对象内的值还是可以被修改的~

public class TestFinal {void execute(int s){s = s+3; System.out.println(s);}public static void main(String args[]) {final int ii = 3;testFinal tf = new testFinal();tf.execute(ii);}}
结果是:6

因为我们只是把ii的值3传给对象s,而不是s就是ii对象,此时将execute参数变成final int s,此时s就不能改变

String, Math和一些基础类型包装类都是final类型的

二十五、接口
关键字interface, implements

接口是对实物的属性和行为更高层次的抽象,是抽象方法和常量值的定义的集合,从本质上看,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。多个无关的类可以实现同一个接口,而且一个类可以实现多个接口,实现多态性,接口可以继承其他的接口,添加新的属性和抽象方法,采取对修改关闭,对扩展开放得开闭原则

接口中的属性默认都是public static final的,为什么??

- 首先我们定义接口属性,希望别人能用上,是所有类都可以拥有的属性,如果具体实现类不能使用该属性,不就没有意义了嘛(同理,接口和接口方法也都是默认public)

- static的话,所有类共有的特性,如果非static的话,只有类创建实例的时候才能拥有这些方法,那么就不能被继承啦,并且一个类可以继承多个接口,如果出现重名不好区分(

public Interface Interface1 {

void getDesc();

}

public Interface Interface2 {

void getDesc();

}

public class TestInterface implements Interface1, Interface2 {

void getDesc(); //因为getDesc是静态的,不会出错

}


但是如果A接口底下有一个double getMoney()方法,而B接口下有一个void getMoney()的方法,此时一个类同时实现这两个接口,就不好办了)

http://bbs.itheima.com/thread-162971-1-1.html

- 接口的成员不能修改,如果不是final的话,子类就可以修改,不符合开闭原则 ,就想我们有一个模板,希望后面可以添加功能,并不修改本来的功能

interface Singer {void sing();}interface Painter {void paint();}class Student3 implements Singer {public void sing() {System.out.println("sing3");}}class Student4 implements Singer, Painter {public void paint() {System.out.println("paint4");}public void sing() {System.out.println("sing4");}}public class TestInterface {public static void main(String args[]) { Singer s1 = new Student3();s1.sing();Singer s2 = new Student4();s2.sing();//s4.paint(); 执行错误Painter p = (Painter)s2;//p.sing(); 执行错误p.paint(); }}




记下一些MAC快捷键:

cmd+m 

cmd+n

cmd+f

shift+cmd+g

cmd+tab


Reference:

1. 马士兵JAVA基础视频

0 0
原创粉丝点击