类 & 对象

几篇比较好的博文   点击打开链接:类与继承    点击打开链接:接口与抽象类   点击打开链接:abstract与static  继承多态重写重载


1.关于final

        表明这是一个常量,值不变。

        成员变量若被声明称final,定义时必须被赋值,因为成员变量随着类的初始化而初始化,初始化时没有值的常量是要报错的

        而方法内的局部变量被声明成final则没有这个问题,因为方法未必会在类初始化时调用,常量可以先声明,再赋值

        不能被继承,不能被重写,只能赋值一次


2.关于static

       static是静态的意思,表明被声明的变量或者方法在内存中只此一份,他就被大家共享了,不论谁改了他,别人在调用时这个值都已经变了

        类的一个成员被static修饰后,该成员就属于类的成员了,被所有对象共享,如果成员是成员变量,那么该成员以及该成员的值就被所有对象所共享;如果成员是成员方法,那么该方法就被所有对象共享;相反如果成员没有用static修饰,那么每个对象就有自己的成员,这时可以说,每个对象的成员是他私有的,注意这和权限控制里的私有权限是两码事。可以想象,共享的方法只能访问共享的成员,是无法访问对象私有的成员的,而对象私有的成员方法是可以访问共享的成员的。打个比方,就像我们给公家干活肯定花的是公家的钱,绝不会花私人的钱,而给自己干活时,却可以花公家的钱,只要公家不追究。这也就是为什么static修饰的成员只能访问static修饰的成员,如static修饰的成员方法,static修饰的初始化块还有static修饰的内部类均如此。

 

         在实际开发中,之所以给类定义成员变量,就是因为该类有不同状态的对象,比如我们定义人这个类,一般会定义年龄、姓名等属性,然后不同的人(这里就是人这个类的对象了)虽然都有年龄和姓名,但各有各的年龄,各有各的姓名,这就是上面说的每个对象的成员是该对象私有的。如果把年龄和姓名定义成static的,那就导致所有的人都有相同的年龄和姓名,显然这没有意义。

        通常,我们在定义一个工具类时,一般把它的方法定义成static的,因为这种类只用作工具,只关注他的行为,不关注他的状态,所以不需要定义成员变量。使用这种工具类的方法时无需创建对象,既简单又节省资源。创建对象来调用反而麻烦且浪费资源,所以这种类被设计出来后就干脆不允许创建对象,因为其构造方法被设计成private权限了。比如我们用的Math和Arrays,还有Collections。这三个类时我们java中最常见的三个工具类。

         静态的可以直接用类名去调用,而无需先实例化对象

         静态方法可以被继承,但是当子类里有同名的时候,不论是静态还是非静态,父类的静态方法都是被隐藏,而不是被重写或者覆盖,想要访问需要有对象才可以

         


我们在用final定义常量时,习惯于再加上一个static,这就是出于节省内存的考虑,静态,大家共享。


      程序最终都将在内存中执行,变量只有在内存中占有一席之地时才能被访问。

      当new 一个对象的时候,并不是先在堆中为对象开辟内存空间,而是先将类中的静态方法(带有static修饰的静态函数)的代码加载到一个叫做方法区的地方,然后再在堆内存中创建对象。所以说静态方法会随着类的加载而被加载。当你new一个对象时,该对象存在于堆内存中,this关键字一般指该对象,但是如果没有new对象,而是通过类名调用该类的静态方法也可以

       类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存(故而会先看有没有静态的,有就先加载,没有就看别的),可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。

      在一个类的静态成员中去访问其非静态成员之所以会出错是因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。

       而类又是在什么时候加载的呢?

       由引导类加载器负责加载的核心类比如 String 类在 JVM 启动时(main 方法开始执行前)就会被加载,其它类在使用前(new 它对象或调用其静态方法访问静态域等等前)会被动态加载,要注意的是子类被加载前它的所有超类要根据由父到子的顺序被逐一加载


3.封装

       类的存在就是封装的一种表现,用类来封装相关的数据和 方法,保证了数据的安全和系统的严密性。


4.继承

        简单说  子 is a 父

        父类有的,子类的都应该有

        父类有的抽象方法(抽象就意味着必须实现),子类都必须实现,一般的可以看需求而定;另外子类可以选择重写,或者说覆盖,也就是说对父类的函数进行重新定义

  有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写,overriding

 子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。

  如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。


注:关于初始化,一个类肯定是必须要有构造函数来完成初始化的。   

      子类对象的成员初始化之前必须完成父类或祖先类对象的成员的初始化,初始化的方式有两种:隐式初始化显式初始化

       隐式初始化:当父类提供了默认的构造函数,且子类的构造函数中没有显式调用父类的其它构造函数,则在执行子类的构造函数之前会自动执行父类的构造函数,直到执行完Object的构造函数后才执行子类的构造函数

      显式初始化当父类没有提供默认构造函数时,必须在子类构造函数第一条语句通过super(…)完成父类对象成员的初始化工作
                          保留字super可以在任意的非静态方法中使用
                          super可以看作当前对象实例所对应的父类型的实例引用,通过super引用可以访问属于父类实例的域和方法      
                          被final修饰的类不能被继承,被final修饰的方法不能被覆盖




5.多态

用同一个名字调用实现不同操作即为多态,他有两种表现形式
(1) 即是上面所说的重写,子类与父类的同名方法
(2)重载。有一堆同名但参数不同的方法,更具实际需求的不同选择不同的重载方法

实际上,父类的引用指向了子类的对象
即 Father f = new Child();

对于成员变量和静态方法看左边,在编译、运行的时候编译器不管右边是什么,只要左边能运行或者编译即可

对于非静态方法:编译看左边,运行看右边,因为存在重写的情况,所以运行时要看右边,看子类重写的方法


此时不能使用子类特有功能

要么 创建子类对象再调用特有方法;要么把父类引用强转为子类引用


在实际应用中,就是父类根据不同的子类调用不同的子类的方法,扩展性强~


6.接口
   是为实现多重继承的一种特殊机制。
     接口可以理解为一个特殊的类,只有常量抽象方法组成的特殊类。
    每个接口一般也是定义在.java文件中
 public interface Collection
{
int MAX_NUM=100;
void add(int a);
void delete(int b);
int find(int c);
int currentCount();
}

     接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。


接口的实现,简单说,子 can do 父

实现接口,必须实现它所有的方法,子类是抽象类除外,可以实现部分接口的方法
也即是说,
 接口的子类
 要么是抽象类
 要么重写接口中所有的抽象方法

接口可以继承接口,并且是多亲继承,
可以在关键字extends后面跟多个接口的列表,中间用逗号隔开;
子接口拥有所有父接口中声明的方法


类必须提供接口中所有方法的实现,如果有方法在类中没有实现,则类必须声明为抽象类

接口即默认是抽象,无需再添上abstract了(普通的方法加abstract表明这个必须要被实现,接口里的方法本身已经默认必须全部实现),


7.内部类 & 匿名对象

匿名对象:没有名字的对象, 是对象的一种简化表现形式

匿名对象的使用有两种情况
 对象调用方法仅有一次,不适合调用多次
 作为实际参数传递

 内部类——将一个类定义在另一个类的内部

 内部类的访问特点
 内部类可以直接访问外部类 的所有成员,包括私有成员
 外部类如需访问内部类的成员,需要创建对象
 外界如何创建内部类对象?
 外部类名.内部类名 对象名 =new 外部类对象.内部类对象


8.抽象类

比如,动物是抽象类,猫狗是具体对象,抽象类里不能有猫狗特有的属性,故我们不应该在动物类给出具体的实现,而是应该给出一个行为的声明即可

一个没有方法体的方法应该定义为抽象方法,而如果一个类中含有抽象方法,则该类必须定义为一个抽象类
 抽象类不一定有抽象方法,但是含有抽象方法的类必须是抽象类
 抽象类不能实例化(但是多态机制可以用子类实例化)

 抽象类的子类:
如果不想重写抽象类里面的抽象方法,则也必须是抽象类
如果不是抽象类,则必须实现抽象父类的所有抽象方法

抽象类的方法:可以是抽象的,也可以是非抽象的,作用是:
抽象的:强制子类实现
非抽象的:子类可以复用

举个例子来说说继承和多态
一个动物类,有吃和睡的行为
猫和狗继承于动物类,在继承的时候,我们可以只实现某一个行为(方法),当然,如果在父类里给这些行为加上了abstract,那就必须实现了

public class AnimalDemo {
   public static void main(String[] arg){
	   Cat cat1 = new Cat();
	   cat1.eat();
	   cat1.sleep();
	   
	   Cat cat2 = new Cat();
	   cat2.eat();
	   cat2.sleep();
	   
	   Dog dog1 = new Dog();
	   dog1.eat();
	   dog1.sleep();
   }
}
//父类,能吃能睡
class Animal{
	String name = "animal";
	public void eat(){
		System.out.println("Animal.eat()");
	}
	public void sleep(){
		System.out.println("Animal.sleep()");
	}
}
//子类猫,继承了吃睡
class Cat extends Animal{
	public void eat(){
		System.out.println("cat.eat fish");
	}
	public void sleep(){
		System.out.println("cat.sleep()");
	}
}
class Dog extends Animal{
	public void eat(){
		System.out.println("dog.eat meat");
	}
}

此处,子类dog里没有实现sleep方法,但是却调用了这个方法,也可以,但实际调用的是父类的sleep方法



对于每个实例猫,都有同样的操作,可以抽出来,好比有两个饲养员,一个养猫,一个养狗

public class AnimalDemo {
   public static void main(String[] arg){
	   Cat cat1 = new Cat();
	   keepcat(cat1);
	   
	   Cat cat2 = new Cat();
	   keepcat(cat2);
	   
	   Dog dog1 = new Dog();
	   keepDog(dog1);
   }
   public static void  keepcat(Cat c){
	 c.eat();
	 c.sleep();
   }
   public static void  keepDog(Dog d){
	 d.eat();
   }
}

//父类,能吃能睡
class Animal{
	String name = "animal";
	public void eat(){
		System.out.println("Animal.eat()");
	}
	public void sleep(){
		System.out.println("Animal.sleep()");
	}
}
//子类猫,继承了吃睡
class Cat extends Animal{
	public void eat(){
		System.out.println("cat.eat fish");
	}
	public void sleep(){
		System.out.println("cat.sleep()");
	}
}
class Dog extends Animal{
	public void eat(){
		System.out.println("dog.eat meat");
	}
}

再加一个子类bear,甚至更多,也可以类似操作

但是这里可以更方便的操作,来一个饲养员,只要给我一个动物,我就能养,不论这是什么动物

也就说 父类的引用指向了子类的对象

public class AnimalDemo {
   public static void main(String[] arg){
	   Cat cat1 = new Cat();
	   keepanimal(cat1);
	   
	   Cat cat2 = new Cat();
	   keepanimal(cat2);
	   
	   Dog dog1 = new Dog();
	   keepanimal(dog1);
	   
	   Bear bear1 = new Bear(); 
	   keepanimal(bear1);
	   
	   //多态,父类引用指向了子类的对象,调用了子类的方法
	   Animal animal1 = new Cat();
	   keepanimal(animal1);
	   //匿名内部类
	   keepanimal(new Bear());   
   }
 public static void keepanimal(Animal a){
	   a.eat();
	   a.sleep();
 }
}

//父类,能吃能睡
class Animal{
	String name = "animal";
	public void eat(){
		System.out.println("Animal.eat()");
	}
	public void sleep(){
		System.out.println("Animal.sleep()");
	}
}
//子类猫,继承了吃睡
class Cat extends Animal{
	public void eat(){
		System.out.println("cat.eat fish");
	}
	public void sleep(){
		System.out.println("cat.sleep()");
	}
}
class Dog extends Animal{
	public void eat(){
		System.out.println("dog.eat meat");
	}
	public void sleep(){
		System.out.println("dog.sleep()");
	}
}

class Bear extends Animal{
	public void eat(){	
		System.out.println("Bear.eat honey");
	}
	public void  sleep(){
		System.out.println("Bear.sleep()");		
	}		
}


倘若此时 子类dog有一个特有的看家的方法,keephouse(),这个时候不能直接在keepAnimal里调用这个方法,饲养员只能有通用的技能,狗狗太另类
怎搞

要么强转成子类,要么创建子类对象

创建子类对象

//创建子类对象,可以直接调用子类的方法,也可以通过饲养员调用
	   Dog animal3 = new Dog();
	   /*animal2.eat();
	   animal2.sleep();*/
	   keepanimal(animal2);
	   animal3.keepHouse();

强转

Animal animal2 = new Dog();
	   /*animal2.eat();
	   animal2.sleep();*/
	   keepanimal(animal2);
	   /*//多态里无法直接引用子类特有方法,可强转
	   animal2.keepHouse();*/
	   ((Dog)animal2).keepHouse();


public class AnimalDemo {
   public static void main(String[] arg){
	   Cat cat1 = new Cat();
	   keepanimal(cat1);
	   
	   Cat cat2 = new Cat();
	   keepanimal(cat2);
	   
	   Dog dog1 = new Dog();
	   keepanimal(dog1);
	   
	   Bear bear1 = new Bear(); 
	   keepanimal(bear1);
	   
	   //多态,父类引用指向了子类的对象,调用了子类的方法
	   Animal animal1 = new Cat();
	   keepanimal(animal1);
	   
	   //匿名内部类
	   keepanimal(new Bear());   
	   
	   Animal animal2 = new Dog();
	   /*animal2.eat();
	   animal2.sleep();*/
	   keepanimal(animal2);
	   /*//多态里无法直接引用子类特有方法,可强转
	   animal2.keepHouse();*/
	   ((Dog)animal2).keepHouse();
	   
	   //创建子类对象,可以直接调用子类的方法,也可以通过饲养员调用
	   Dog animal3 = new Dog();
	   /*animal2.eat();
	   animal2.sleep();*/
	   keepanimal(animal2);
	   animal3.keepHouse();
	   
   }
 public static void keepanimal(Animal a){
	   a.eat();
	   a.sleep();
 }
}

//父类,能吃能睡
class Animal{
	String name = "animal";
	public void eat(){
		System.out.println("Animal.eat()");
	}
	public void sleep(){
		System.out.println("Animal.sleep()");
	}
}
//子类猫,继承了吃睡
class Cat extends Animal{
	public void eat(){
		System.out.println("cat.eat fish");
	}
	public void sleep(){
		System.out.println("cat.sleep()");
	}
}
class Dog extends Animal{
	public void eat(){
		System.out.println("dog.eat meat");
	}
	public void sleep(){
		System.out.println("dog.sleep()");
	}
	public void keepHouse(){
		 System.out.println("dog.keep house()!");		
	}
}

class Bear extends Animal{
	public void eat(){	
		System.out.println("Bear.eat honey");
	}
	public void  sleep(){
		System.out.println("Bear.sleep()");		
	}		
}

我们可以把JAVA中的类分为以下三种: 


       类:使用class定义且不含有抽象方法的类。
      抽象类:使用abstract class定义的类,它可以含有,也可以不含有抽象方法。
      接口:使用interface定义的类。


在这三种类型之间存在下面的继承规律: 
       类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
       抽象类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
       接口只能继承(extends)接口。


        请注意上面三条规律中每种继承情况下使用的不同的关键字extends和implements,它们是不可以随意替换的。大家知道,一个普通类继承一个接口后,必须实现这个接口中定义的所有方法,否则就只能被定义为抽象类。我在这里之所以没有对implements关键字使用“实现”这种说法是因为从概念上来说它也是表示一种继承关系,而且对于抽象类implements接口的情况下,它并不是一定要实现这个接口定义的任何方法,因此使用继承的说法更为合理一些。


以上三条规律同时遵守下面这些约束:
         类和抽象类都只能最多继承一个类,或者最多继承一个抽象类,并且这两种情况是互斥的,也就是说它们要么继承一个类,要么继承一个抽象类。
         类、抽象类和接口在继承接口时,不受数量的约束,理论上可以继承无限多个接口。当然,对于类来说,它必须实现它所继承的所有接口中定义的全部方法。
         抽象类继承抽象类,或者实现接口时,可以部分、全部或者完全不实现父类抽象类的抽象(abstract)方法,或者父类接口中定义的接口。
         类继承抽象类,或者实现接口时,必须全部实现父类抽象类的全部抽象(abstract)方法,或者父类接口中定义的全部接口。


        重写,也就是覆盖,英文名是overriding,是指在继承情况下,子类中定义了与其基类中方法具有相同型构的新方法,就叫做子类把基类的方法重写了。这是实现多态必须的步骤。比如对接口方法的实现,接口里的方法一般只有方法名,没有方法体,需要子类自己去重写实现
        重载,英文名是overloading,是指在同一个类中定义了一个以上具有相同名称,但是型构不同的方法。在同一个类中,是不允许定义多于一个的具有相同型构的方法的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值