懵懂的菜鸟

Stay hungry,Stay foolish.

导航

java面向对象

1,嵌套类(内部类)nested class(inner class)

 

java内部类与c++嵌套类最大的不同就在于是否有指向外部的引用。

 

创建一个static内部类的对象,不需要一个外部类对象,不能从一个static内部类的一个对象访问一个外部类对象。

内部类或嵌套类在类层级上没有限制,内部类可以是私有类。

2,集合类

java中的容器类库一共有两种主要类型:Collection和Map

collection的每个槽内只保存一个元素;map的每个槽内保存的是键值对

collection包括list,set,Queue,

  list:Arraylist,LinkedList,Vector;

    list:将以特定次序存储元素。ArrayList:擅长随机访问,擅长插入、删除和移动。

    vector:向量类(vector) 实现类似动态数组的功能。创建了一个向量类的对象后,可以往其中随意插入不同类的对象,即不需顾及类型也不需预先选定向量的容量,并可以方便地进行查找。对于预先不知或者不愿预先定义数组大小,并且需要频繁地进行查找,插入,删除工作的情况。可以考虑使用向量类。

  set:Hashset,Treeset,linkedHashset;

    set:不含重复的元素。Hashset:使用散列函数;TreeSet:使用红黑树。linkedHashset:使用链表结构的散列函数。

  queue:Priorityqueue;

    queue:先进先出的容器。

Map包括HashMap,HashTable,TreeMap

  java中的map都不在重复的key。

   hashmap:线程不安全,适合在map中插入、删除和定位元素。没有分类或者排序,它允许一个null键和多个null值。

   hashtable:线程安全。安全一般意味着效率低,同步。它不允许null键和null值。

   Treemap:适合按自然顺序或自定义顺序遍历键,通常比Hashmap速度慢,在需要排序的map时才使用。

 

3,构造函数和析构函数

重写(覆盖),重载

覆盖又叫重写,因此重写和覆盖是一个概念。它是覆盖了一个方法并且对其重写,以求达到不同的作用。形式有:对接口方法的实现,在继承中也可能会在子类覆盖父类中的方法。

重载:它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Parent{//定一个类
   public void read(){
   }
   public void show(){//重载了show方法
   }
   public void show(int a){//重载了show方法,比第一个方法多了一个参数
   }
}
 
public class Son extends Parent{//son类继承父类parent
   public void read(){//覆盖了父类的read方法。
   }
}
重载构造方法是可以的。
但是重写则不可以,因为被重写的前提是被继承,而构造方法根本就不能被继承,所以谈不上被重写。

 

构造函数的格式

  了解了构造函数的基本概念,现在来写一个构造函数,希望大家可以了解、记忆其格式,通过实例发现其与普通函数的不同之处。

01  public class Demo{

02     private int num=0;

03     //无参构造函数

04     Demo()

05    {

06      System.out.println("constractor_run");

07    }

08     //有参构造函数

09     Demo(int num)

10    {

11      System.out.println("constractor_args_run");

12    }

13     //普通成员函数

14    public void demoFunction()

15    {

16      System.out.println("function_run");

17    }

18  }

构造函数与普通函数的区别

  下面来详细的分析下构造函数与普通函数的区别,通过两者的一个对比,希望可以加深对构造函数的概念的理解。

    1)  格式不同:

      构造函数不存在返回类型,函数名与所在类的类名一致;

      普通函数有返回类型,函数名可以根据需求进行命名。

    2)调用时期不同

      构造函数在类的对象创建时就运行;

      普通函数在对象调用时才会执行。

    3)执行次数不同

      一个对象创建后,其构造函数只执行一次,就是创建时执行;

      一个对象创建后,其普通函数可以执行多次,取决于对象的调用次数。

java派生类被构造时一定先调用父类的构造函数

子类可以通过super关键字来显式地调用父类的构造函数。

当父类没有提供无参数的构造函数时,子类的构造函数中必须显式的调用父类的构造函数;

如果父类提供了无参数的构造函数,此时子类的构造函数就可以不显式的调用父类的构造函数,默认调用父类的无参构造函数。

 

[java] view plain copy
 
  1. package com.bjut.StudyTest;  
  2.   
  3. class Person {  
  4.     public Person() {  
  5.         System.out.println("Base has no args.");  
  6.     }  
  7.   
  8.     public Person(String temp) {  
  9.         System.out.println("Base:" + temp);  
  10.     }  
  11. }  
  12.   
  13. class Student extends Person {  
  14.     public Student() {  
  15.         super("a");  
  16.         System.out.println("Student has no args.");  
  17.     }  
  18.   
  19.     public Student(String temp) {  
  20.         System.out.println("Student:" + temp);  
  21.     }  
  22. }  
  23.   
  24. public class TestConstruction {  
  25.   
  26.     public static void main(String[] args) {  
  27.         Person p = new Student(); // 先调用父类的构造函数,显示调用指定的父类构造函数。  
  28.         Student s = new Student("b"); // 先调用父类的构造函数,默认调用父类无参构造函数。  
  29.     }  
  30.   
  31. }  

输出:

 

 

[plain] view plain copy
 
  1. Base:a  
  2. Student has no args.  
  3. Base has no args.  
  4. Student:b  


当有父类时,在实例化对象时会先执行父类的构造函数,然后执行子类的构造函数。

 

(补充)java 程序初始化工作执行的顺序:

     父类静态变量 -> 父类静态代码块 -> 子类静态变量 -> 子类静态代码块

 -> 父类非静态变量 -> 父类非静态代码块 -> 父类构造函数

 -> 子类非静态代码块 -> 子类非静态变量 -> 子类构造函数

 

注意:constructor在一个对象被new时执行。

public class Z extends X{//步骤1:先调用X,再调用Z。

  Y y=new Y();//步骤2:接着构造Y

  Z(){//步骤3:最后构造自身Z

    System.out.print("z");
  }

  public static void main(String[] args){

    new Z();

  }

}

析构函数在java中常见的就是finalize()函数,GC垃圾回收机制,需要程序员进行重写。

 

 浅拷贝,即在定义一个类A,使用类似A obj;  A obj1(obj);或者A obj1 = obj; 时候,由于没有自定义拷贝构造函数,C++编译器自动会产生一个默认的拷贝构造函数。这个默认的拷贝构造函数采用的是“位拷贝”(浅拷贝),而非“值拷贝”(深拷贝)的方式,如果类中含有指针变量,默认的拷贝构造函数必定出错。

用一句简单的话来说就是浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

 

多态

 

多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态的作用:消除类型之间的耦合关系。

 

 多态有两种表现形式:重载和覆盖

 

java中的动态绑定和静态绑定(或者叫做前期绑定和后期绑定)

 

我们发现java属于后期绑定。在java中,几乎所有的方法都是后期绑定的,在运行时动态绑定方法属于子类还是基类。但是也有特殊,针对static方法和final方法由于不能被继承,因此在编译时就可以确定他们的值,他们是属于前期绑定的。特别说明的一点是,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的(由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。java中的后期绑定是有JVM来实现的,我们不用去显式的声明它,而C++则不同,必须明确的声明某个方法具备后期绑定。

 

 

 

 

 

java当中的向上转型或者说多态是借助于动态绑定实现的,所以理解了动态绑定,也就搞定了向上转型和多态。

 

对于java当中的方法而言,除了final,static,private和构造方法是前期绑定外,其他的方法全部为动态绑定。而动态绑定的典型发生在父类和子类的转换声明之下:
比如:Parent p = new Children();

 

与方法不同,在处理java类中的成员变量(实例变量和类变量)时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性(成员变量)还是父类的属性(子类对父类成员变量的隐藏)。
Java代码 

 

[java] view plain copy

 

  1. public class Father {  
  2.     protected String name = "父亲属性";  
  3. }  
  4.   
  5.   
  6. public class Son extends Father {  
  7.     protected String name = "儿子属性";  
  8.   
  9.     public static void main(String[] args) {  
  10. 10.         Father sample = new Son();  
  11. 11.         System.out.println("调用的属性:" + sample.name);  
  12. 12.     }  

13. }  

 


结论,调用的成员为父亲的属性。
这个结果表明,子类的对象(由父类的引用handle)调用到的是父类的成员变量。所以必须明确,运行时(动态)绑定针对的范畴只是对象的方法
现在试图调用子类的成员变量name,该怎么做?最简单的办法是将该成员变量封装成方法getter形式
代码如下:
Java代码 

 

[java] view plain copy

 

  1. public class Father {  
  2.     protected String name = "父亲属性";  
  3.   
  4.     public String getName() {  
  5.         return name;  
  6.     }  
  7. }    
  8.   
  9. public class Son extends Father {  
  10. 10.     protected String name = "儿子属性";  
  11. 11.   
  12. 12.     public String getName() {  
  13. 13.         return name;  
  14. 14.     }  
  15. 15.   
  16. 16.     public static void main(String[] args) {  
  17. 17.         Father sample = new Son();  
  18. 18.         System.out.println("调用的属性:" + sample.getName());  
  19. 19.     }  

20. }  

 


结果:调用的是儿子的属性
java因为什么对属性要采取静态的绑定方法。这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态,多态是java的一大特色。多态也是面向对象的关键技术之一,所以java是以效率为代价来实现多态这是很值得的

 

posted on 2017-10-23 12:18  懵懂的菜鸟  阅读(333)  评论(0编辑  收藏  举报