java笔记--类与继承

Java核心概念详解
1、静态域
如果将域定义为静态static,则该域属于类,而不属于任何一个对象。该类的所有对象共享这个域。
例如:
class Employee
{
private static int nextid=1;
private int id;
}
则该类的每一个对象都有一个自己的id域,即是1000个对象就有1000个实例域id,但是却只一个静态域nextid,它被所有对象所共享。
2、静态方法
静态方法不可以对对象实施操作,它没有隐式参数this。比如上面这个Employee类,静态方法getNextId()不能访问id实例域,却可以访问自身类中的静态域nextid。
public static int getNextId()
{
return nextid;
//return id;//ERROR 不能访问非静态域
}
调用静态方法是使用类名调用,如Employee.getNextId(),也可以使用对象调用(不建议)。假如一个方法不需要访问对象状态,或者只需要访问类的静态域,则应该使用静态方法。
3、初始化代码块
先来看一个例子:
class Employee
{
private static int nextid;
private int id;
private String name;
......
//initial block
{
id=nextid;
nextid++;
}
......
}
在这个类中有一个初始化代码块,在构造该类对象时就会被执行,而且一个类中可以包含多个这样的代码块。把代码放在一个块中,并标记关键字static,便是静态代码块。如下:
static
{
Random generator=new Random();
nextid=generator.nextInt();
}

该静态代码块在类加载时就会被执行。构造对象时一般有如下几个可以被初始化的地方:

(1)静态初始化代码块

(2)默认初始化

(3)初始化代码块

(4)构造方法

一般来讲,对象构造时执行的步骤如下:

(1)在类第一次被加载的时候的,静态初始化模块会工作完成一个部分初始化任务
(2)在类构造对象的时候,具体步骤如下:
    a、系统创建类的实例对象
    b、执行默认初始化,如果程序中指定了默认初始化的值,那么就按照默认值进行初始化,如果程序中没有指定初始化的值,那么由系统自动赋值,赋值的原则为:“一切都是0”,整型属性赋值0,浮点属性赋值0.0,字符属性赋值‘/0',布尔类型属性赋值false,引用类型属性赋值null
    c、执行初始化模块,如果程序中存在多个初始化模块,那么按照书写顺序执行所有初始化模块。
    d、执行对象中的构造方法。


4、所有类的超类Object
Object类是java中所有类的始祖,在java中每个类都是由它扩展而来,因此可以使用Object类型的变量引用任何类型的对象,如:Object obj=Employee("harry",3500);Object类有几个很重要的方法,有equals方法,hashcode方法,toString方法
1)equals方法:
public boolean equals(Object obj){
    return (this == obj);
}
该方法实际上比较的是对象的引用,也即对象的地址值。如果两个对象为同一个对象则返回true,否则返回false。一般来讲,继承该类的子类都要重写equals方法。
2)hashcode方法:
public native int hashCode(); 由此可知,该方法是本地方法,Object类中没有给具体的实现。一般说来,继承该类的子类都要重写equals方法。
两者的关系:
如果equals相等,则hashCode一定相等。
hashCode相等,equals不一定相等。因为从散列表的角度考虑,不一样的对象,其hash值很有可能相同。
一个典型的面试例子:
class HashCodeDemo
{
public static void main(String[] args) 
{
String a="abc";
String b="abc";
String c="abcabc";
String d="abc"+"abc";
String e=a+b;
String f=new String("abc");


System.out.println(a==b); //true  
System.out.println(c==d); //true 
System.out.println(c==e); //false 没想明白,记住
System.out.println(a==f); //false

System.out.println();

System.out.println(a.equals(b)); //true
System.out.println(c.equals(d)); //true
System.out.println(c.equals(e)); //true
System.out.println(a.equals(f)); //true


System.out.println();

System.out.println(a.hashCode()==b.hashCode()); //true
System.out.println(c.hashCode()==d.hashCode()); //true
System.out.println(c.hashCode()==e.hashCode()); //true
System.out.println(a.hashCode()==f.hashCode()); //true
}
}
运行该例子,答案见代码注释。String类型重写了equals和hashCode方法,equals方法比较的是字符串的内容是否相同,hashCode方法在计算hash值时是根据内容来计算的,所以内容相同的hashCode也相同。那么a==b 为什么成立呢? 这是由于"abc"字符串是存放在String pool中的,在执行b="abc"时,系统会先到String pool中查看是否有该字符串,如果有,则不创建新的对象,直接将b指向该字符串。那么c==d为什么成立呢?我的理解是"abc"+"abc","abc" 以及"abcabc"String pool中都有,因此d也直接指向"abcabc"。
3)toString方法:
看一下Object类中的toString()方法源码: 
public String toString() { 
return getClass().getName() + "@" + Integer.toHexString(hashCode()); 

你的类可以复写了Object类(此类为Java根基类)中的toString()方法 如: 
public String toString(){ 
return "Hello"; //这里才是你要返回的值 如果没复写 则调用Object类中的toString()方法(打印类的全限命名+内存地址) }

5、类的继承
利用继承关系(is-a),可以基于已经存在的类构造新类,继承已经存在的类就是复用该类的方法和域,并在此基础上添加一些新的方法和域,以满足新的需求。值得注意的是,继承关系是一种is-a关系,如下例子,经理类继承自员工类,每一个经理都是员工(is-a)。
import java.util.*;
import java.util.GregorianCalendar;
class InheritanceDemo
{
public static void main(String[] args)
{
Employee[] employee=new Employee[3];
Manager boss=new Manager("xiaoming",40,new GregorianCalendar(2015,1,1).getTime());
boss.setBonus(1000);
boss.setSalary(10000);


employee[0]=boss;
employee[1]=new Employee("jack",30,new GregorianCalendar(2014,1,1).getTime());
employee[2]=new Employee("mary",26,new GregorianCalendar(2013,1,1).getTime());
employee[1].setSalary(7000);
employee[2].setSalary(5000);


for(Employee e : employee)
{
System.out.println("name="+e.getName()+",age="+e.getAge()+ ",hireDay="+ e.getHireDay()+",salary="+e.getSalary());
}
}
}


class Employee
{
private int age;
private String name;
private double salary;
private Date hireDay;
//initial block
{
this.age=10;
this.name="lisi";
this.salary=0;
this.hireDay=new GregorianCalendar(2012,10,1).getTime();
}


public Employee(String name,int age,Date hireDay)
{
this.name=name;
this.age=age;
this.hireDay=hireDay;
this.salary=0;
}


public int getAge()
{
return this.age;
}
public String getName()
{
return this.name;
}
public double getSalary()
{
return this.salary;
}
public void setAge(int age)
{
this.age=age;
}
public void setName(String name)
{
this.name=name;
}
public void setSalary(double salary)
{
this.salary=salary;
}
public void raiseSalary(double byPercent)
{
double raise=this.salary*byPercent/100;
this.salary+=raise;
}
public Date getHireDay()
{
return this.hireDay;
}
}


class Manager extends Employee
{
private double bonus;
public Manager(String name,int age,Date hireDay)
{
super(name,age,hireDay);
bonus=0;
}


public void setBonus(double bonus)
{
this.bonus=bonus;
}
public double getSalary()
{
return super.getSalary()+bonus;
}
}


在新类Manager的构造函数中,super(name,age,hireDay);是在调用基类的构造函数,如果需要调用基类的方法同样可以使用super关键字,如super.getSalary()就是调用
基类的getSalary()方法。
6、多态
一个多态的典型例子:
Employee e;
e=new Employee(...);
e=new Manager(...);
员工类变量既可以引用本类对象,还可以引用其任何子类对象,这就是对象变量的多态性。然而不能将一个基类对象赋给子类变量,如 Manager m=new Employee(...);这是非法的,原因显而易见,不是每一个员工都是经理。
7、静态绑定和动态绑定
静态绑定:在面向过程的中又称为前期绑定在程序编译时进行了绑定,即在还没运行时,就已经加载到内存。
动态绑定:在面向过程中称为后期绑定(运行时绑定)在运行时就进行绑定,根据实际情况有选择的进行绑定。
动态绑定灵活性高,但是由于它是在运行的时候进行绑定,因此效率相对低些。
重要结论:private方法,static方法,final方法以及构造方法,还有java中的变量均使用静态绑定。

8、抽象类
一个抽象出来的超类有多个子类,这些子类都有一个共同的方法(因此应该在基类中),但是对基类来说,该方法在每个子类中具体如何实现基类一无所知,这个时候应该将该方法声明为抽象(abstract)的。如下例子:
abstract class Person
{
...
public abstract String getDescription();
}
由于方法是抽象的,因此该类也必须声明为抽象类。当然类即使不含抽象方法,也可以申明为抽象类。抽象类一个重要的特征就是它不能被实例化,也就是说表达式new Person("Vince vu")是错误的,因为不能创建一个抽象类对象。然而可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象,例如:
Person p=new Student();--Student是Person的非抽象子类。一个典型的实例代码:
class AbstractDemo
{
public static void main(String[] args)
{
People p=new Employee();
Employee e=(Employee)p;
e.getDiscription();
}
}

abstract class People
{
private String name;
private int age;
{
name="Lenuy";
age=26;
}
/*
abstract method
*/
public abstract void getDiscription();
}

class Employee extends People
{
public void getDiscription()  //实现基类抽象方法
{
System.out.println("i am an employee");
}
}


class Student extends People
{
public void getDiscription()
{
System.out.println("i am a Student");
}
}

9、自动装箱和拆箱
自动装箱:把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()、hashCode()、getClass()、equals()等方法。比如:Integer a=1;其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(int i)返回一个表示指定int值的Integer对象,即Integer a=1;   =>    Integer a=Integer.valueOf(1);
拆箱:跟自动装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为基本类型的数据。
如下:
         int i = new Integer(2);//这是拆箱
编译器内部会调用int intValue()返回该Integer对象的int值。
注意:自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。

关于valueOf()方法有点特殊的地方,其源码如下:
public static Integer valueOf(inti) 
{  
    
      if(i >= -128 &&i <=IntegerCache.high)//如果i在-128~high之间,就直接在缓存中取出i的Integer类型对象,不会创建新的对象。


             return IntegerCache.cache[i + 128];  
    
      else
   return new Integer(i); //否则就在堆内存中创建 
   
}
总结:
Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率。
(1)Integer和 int之间可以进行各种比较;Integer对象将自动拆箱后与int值比较
(2)两个Integer对象之间也可以用>、<等符号比较大小;两个Integer对象都拆箱后,再比较大小
(3) 两个Integer对象最好不要用==比较。因为:-128~127范围(一般是这个范围)内是取缓存内对象用,
所以相等,该范围外是两个不同对象引用比较,所以不等。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值