一、修饰符

private 成员随时都是“私有”的,任何人不得访问。但在实际应用中,经常想把某些东西深深地藏起来,但同时允许访问衍
生类的成员。

protected 关键字可帮助我们做到这一点。它的意思是“它本身是私有的,但可由从这个类继承的任何东西或者同一个包内的其他任何东西访问”

采取的最好的做法是保持成员的private 状态——无论如何都应保留对基 础的实施

细节进行修改的权利。在这一前提下,可通过protected 方法允许类的继承者进行受到控制的访问

final关键词

1、final修饰用来表示常量,分为编译期的常量(永不改变)、运行期的常量(每运行一次值会改变)

final可以修饰变量(基本数据变量,引用数据变量--即对象句柄,数组等),都必须显式的赋初值。final修饰对象句柄时,对象句柄初始地指向一个对象后,不能指向其他对象,但是对象本身可以改变。

  1. package lesson12Final关键词;
  2.  
  3. public class FinalData {
  4. // Can be compile-time constants 编译期常数 无论运行多少次,值永远不变
  5. int i0;
  6. final int i1;//被final修饰的变量必须显示的指定初始值
  7. {i1=9;}
  8. final static int i2=99;
  9.  
  10. // Cannot be compile-time constants 不能是编译期间的常数,第二次运行时,值改变
  11. final int i3=(int)(Math.random()*20);
  12. final static int i4=(int)(Math.random()*20);
  13.  
  14. //final修饰对象句柄实时,必须初始化到一个具体的对象,永远不能指向另一个对象;但是对象本身可以改变
  15. Value v1=new Value();
  16. final Value v2=new Value();
  17. final static Value v3=new Value();
  18. //final Value v4;error 不指定初始值出错
  19.  
  20. // Arrays:
  21. final int[] a = { 1, 2, 3, 4, 5, 6 };
  22.  
  23. public static void main(String[] args) {
  24. FinalData data1=new FinalData();
  25.  
  26. //编译期的常量,
  27. //data1.i1++; error final基础数据不能重新赋值
  28. System.out.println("i1:"+data1.i1);
  29. System.out.println("i2:"+i2);
  30. //运行期的常量
  31. System.out.println("data1.i3:"+data1.i3);
  32. System.out.println("i4:"+i4);
  33.  
  34. data1.v1=new Value();
  35. //data1.v2=new Value();error 对象句柄不能重新赋值
  36. int n=++data1.v2.i; //对象本身可以重新赋值
  37. int m=++v3.i;
  38. System.out.println("n:"+n);
  39.  
  40. for(int i = 0; i < data1.a.length; i++)
  41. data1.a[i]++;//对象本身可以重新赋值
  42.  
  43. FinalData data2=new FinalData();
  44. //运行期的常量
  45. System.out.println("data2.i3:"+data2.i3);
  46. System.out.println("i4:"+i4);
  47. }
  48.  
  49. }

运行一次结果:

i1:9
i2:99
data1.i3:16
i4:18
n:2
data2.i3:4
i4:18

两个对象的i4的值一样的,是因为i4是static的,static变量只初始化一次

2、final修饰方法出于对两方面理由的考虑:

第一个是为方法“上锁”,防止任何继承类改变它的本来含义

第二个理由是程序执行的效率:

将一个方法设成final 后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final 方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法【将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理】,相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受不到嵌入代码所带来的任何性能提升

3、final的一个重要用途是来定义宏变量,满足宏变量的充分必要条件是:

必须在定义final变量时初始化,而不是在构造器或者非静态代码块中初始化;

必须编译期就确定了初始值。

static关键词 《基础知识一》第四点

二、合成与继承

合成经典的例子Car---合成一系列组件

因此合成是包含的关系,汽车包含了很多零部件,比如,门,窗,轮子等。

  1. //: Car.java
  2. // Composition with public objects
  3. class Engine {
  4. public void start() {}
  5. public void rev() {}
  6. public void stop() {}
  7. }
  8.  
  9. class Wheel {
  10. public void inflate(int psi) {}
  11. }
  12. class Window {
  13. public void rollup() {}
  14. public void rolldown() {}
  15. }
  16.  
  17. class Door {
  18. public Window window = new Window();
  19. public void open() {}
  20. public void close() {}
  21. }
  22.  
  23. public class Car {
  24. public Engine engine = new Engine();
  25. public Wheel[] wheel = new Wheel[4];
  26. public Door left = new Door(),
  27. right = new Door(); // 2-door
  28. Car() {
  29. for(int i = 0; i < 4; i++)
  30. wheel[i] = new Wheel();
  31. }
  32. public static void main(String[] args) {
  33. Car car = new Car();
  34. car.left.window.rollup();
  35. car.wheel[0].inflate(72);
  36. }
  37. } ///:~

继承是属于的关系,比如汽车是“属于”车辆的,他是车辆的一个类别。

总结:实际中有些时候,需通过“合成”技术用现成的类来构造新类,而继承是最少见的一种做法。尽管继承在学习OOP 的过程中得到了大量的强调,但并不意味着应该尽可能地到处使用它。相反,使用它时要特别慎重。只有在清楚知道继承在所有方法中最有效的前提下,才可考虑它。为判断自己到底应该选用合成还是继承,一个最简单的办法就是考虑是否需要从新类上溯造型回基础类。若必须上溯,就需要继承。但如果不需要上溯造型,就应提醒自己防止继承的滥用。

三、java上溯造型和多形性

何谓绑定?
        绑定是指将一个方法的调用和该方法所在的方法主体(类)关联起来。对于java而言,绑定分为静态(前期)绑定和动态(后期)绑定。
静态绑定:在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。如:C语言
动态绑定:在程序运行时根据具体对象的类型进行绑定。

如果一种语言实现了动态绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的具体类型,但方法调用机制能自行去调查,找到正确的方法主体。不同的语言对动态绑定的实现方法也是不同的。但至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。

  1. public class Base {
  2. public int count=2;
  3. public void display(){
  4. System.out.println("base display:"+this.count);
  5. }
  6. public void show(){
  7. System.out.println("base show:"+this.count);
  8. }
  9. }
  10.  
  11. public class Son extends Base{
  12. int count=20;
  13. public void display(){
  14. System.out.println("son display:"+this.count);
  15. }
  16. }
  17.  
  18. public class Main {
  19. public static void main(String[] args) {
  20. // TODO Auto-generated method stub
  21. Base b=new Base();
  22. System.out.println(b.count);
  23. b.display();
  24.  
  25. Son s=new Son();
  26. System.out.println(s.count);
  27. s.display();
  28. s.show();
  29.  
  30. Base bs=new Son();
  31. System.out.println(bs.count);
  32. bs.display();
  33. bs.show();
  34.  
  35. Base bs2;
  36. bs2=s;
  37. System.out.println(bs2.count);
  38. bs2.display();
  39. bs.show();
  40. }
  41. }
  42.  
  43. 运行结果:
  44. 2
  45. base display:2
  46.  
  47. 20
  48. son display:20
  49. base show:2
  50.  
  51. 2
  52. son display:20
  53. base show:2
  54.  
  55. 2
  56. son display:20
  57. base show:2

上面的程序中,不管是s,bs,bs2变量都是指向了一个Son类型,不管他们在声明时是什么类型,当通过这些变量调用方法时,方法的行为总是表现出他们实际类型的行为;但是通过这些变量访问他们的实例变量,这些实例变量的值总是表现出声明这些bi变量所用类型的行为。

总结:
        在java中,几乎所有的方法都是后期绑定的,在运行时动态绑定方法属于子类还是基类。但是也有特殊,针对static方法和final方法由于不能被继承,因此在编译时就可以确定他们的值,他们是属于前期绑定的。特别说明一点,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的(由此可知,将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。java中的后期绑定是有JVM来实现的,我们不用去显式的声明它,而C++则不同,必须明确的声明某个方法具备后期绑定。
        java当中的向上转型或者说多态是借助于动态绑定实现的,所以理解了动态绑定,也就搞定了向上转型和多态。
  对于java当中的方法而言,除了final,static,private和构造方法是前期绑定外,其他的方法全部为动态绑定。而动态绑定的典型发生在父类和子类的转换声明之下:
  比如:Base b = new Son();
  具体过程如下:
  1. 编译器检查对象的声明类型和方法名。假设我们调用x.f(args)方法,并且x已经被声明为C类的对象,那么编译器会列举出C类中所有的名称为f的方法和从C类的超类继承过来的f方法;
  2. 接下来编译器检查方法调用中提供的参数类型。如果在所有名称为f 的方法中有一个参数类型和调用提供的参数类型最为匹配,那么就调用这个方法,这个过程叫做“重载解析”;
  3. 当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同x所指向的对象的实际类型相匹配的方法版本。假设实际类型为D(C的子类),如果D类定义了f(String)那么该方法被调用,否则就在D的超类中搜寻方法f(String),依次类推。

  运行时动态确定要调用的方法的版本,是根据字节码指令invokevirtual来完成的。需要从这个指令的"多态查找"过程说起。

invokevirtual的运行过程如下:

第一、找到操作数栈顶元素的实际类型,记做C,

第二、如果在C中找到与常量中描述符和简单名称都相同的方法,则进行权限校验,如果通过,则返回这个方法的直接引用,查找过程结束。权限校验不通过则返回java的illegalaccesserror。

第三、如果经过第二步没找到也没抛出异常,则按照继承关系在父类中查找。

第四、如果始终没有找到,则抛出abstractmethoderror。

由于invokevirtual指令在执行的第一步就是确定接收者的实际类型,所以调用的时候,针对不同的调用者(即所说的方法接收者)会把常量池中的类方法解析到不同的直接引用上,这个过程就是java语言“覆盖”父类方法的本质,也就是“重写”的本质。

基础知识《三》java修饰符的更多相关文章

  1. java 基础知识三 java变量

    java  基础知识 三 变量 1.作用域 {} 包围起来的代码 称之为代码块,在块中声明的变量只能在块中使用 2.常量 就是固定不变的量,一旦被定义,它的值就不能再被改变. 3.变量 变量必须在程序 ...

  2. 巩固java(三)---java修饰符

    正文:               下面的表格列出了java中修饰符的一些信息: 修饰符名称 类型 类 变量 方法 abstract 非访问控制符 抽象类          -- 抽象方法 final ...

  3. 《Java基础知识》Java标示符、保留字和数制

    一.Java标识符程序员对程序中的各个元素加以命名时使用的命名记号称为标识符(identifier).Java语言中,标识符是以字母,下划线(_),美元符($)开始的一个字符序列,后面可以跟字母,下划 ...

  4. java的基础语法(标识符 修饰符 关键字)

    Java 基础语法 一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作.下面简要介绍下类.对象.方法和实例变量的概念. 对象:对象是类的一个实例,有状态和行为.例如 ...

  5. JAVA基础语法——标识符、修饰符、关键字(个人整理总结)

    JAVA基础语法——标识符.修饰符.关键字 一 . 标识符 1.1    什么是标识符 就是程序员在定义java程序时,自定义的一些名字,例如helloworld 程序里关键字class 后跟的Dem ...

  6. 2、Java 基础语法标识符、修饰符、变量、 数组、枚举、关键字

    Java 基础语法 一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作.下面简要介绍下类.对象.方法和实例变量的概念. 对象:对象是类的一个实例,有状态和行为.例如 ...

  7. Java基础之Java 修饰符

    前言:Java内功心法之Java 修饰符,看完这篇你向Java大神的路上又迈出了一步(有什么问题或者需要资料可以联系我的扣扣:734999078) Java语言提供了很多修饰符,主要分为以下两类: 访 ...

  8. Java知识回顾 (2) Java 修饰符

    一.Java 修饰符 1.1 访问控制修饰符 Java中,可以使用访问控制符来保护对类.变量.方法和构造方法的访问.Java 支持 4 种不同的访问权限. default (即缺省,什么也不写): 在 ...

  9. Java修饰符关键词大全

    所以我以此主题写了这篇文章.这也是一个可用于测试你的计算机科学知识的面试问题. Java修饰符是你添加到变量.类和方法以改变其含义的关键词.它们可分为两组: 访问控制修饰符 非访问修饰符 让我们先来看 ...

  10. 中高级Java程序员,挑战20k+,知识点汇总(一),Java修饰符

    1 前言 工作久了就会发现,基础知识忘得差不多了.为了复习下基础的知识,同时为以后找工作做准备,这里简单总结一些常见的可能会被问到的问题. 2 自我介绍 自己根据实际情况发挥就行 3 Java SE ...

随机推荐

  1. MFC中获取各个窗口之间的句柄或者指针对象的方法

    MFC在非常多的对话框操作中,我们常常要用到在一个对话框中调用还有一个对话框的函数或变量.能够用例如以下方法来解决.    HWND hWnd=::FindWindow(NULL,_T("S ...

  2. ML:交叉验证Cross-Validation

    PRML中首章绪论的模型选择,提到两个方法: 1.交叉验证(Cross-Validation) 2.赤池信息准则(Akaike Information Criterion),简称:AIC. 交叉验证是 ...

  3. 采集音频和摄像头视频并实时H264编码及AAC编码

    转自:http://www.cnblogs.com/haibindev/archive/2011/11/10/2244442.html 0. 前言 我在前两篇文章中写了DirectShow捕获音视频然 ...

  4. LNMP架构三

    Nginx代理(正向代理) 正向代理:让局域网内的用户 访问外网,外网不能访问局域网, 场景:如果要从国内访问美国的服务器会很慢,这时候就可以找个香港服务器做代理,香港访问美国是很快的. 代理服务器作 ...

  5. git 简单使用规范

    分支管理办法 创建一个主仓库dev 每个成员fork一份dev分支 在自己fork出来的代码里做开发 开发完成后发出一个合并请求 pull request,等待被其他有合并权限的同事合并代码,合并代码 ...

  6. C# DateTime 时间格式

    //2008年4月24日 System.DateTime.Now.ToString("D"); //2008-4-24 System.DateTime.Now.ToString(& ...

  7. poj 3617 Best Cow Line (字符串反转贪心算法)

    Best Cow Line Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9284   Accepted: 2826 Des ...

  8. Redis 的 fields 遇到的问题

    问题描述:本地和测试环境同使用一台redis服务器,本地环境和测试环境使用 key,fileds,value 中的fileds 来区分,例如 key fields value 004920c6eba1 ...

  9. 使用response.setHeader("Content-Disposition","attachment;filename="+fName)下载文件,中文文件名无法显示的问题

    今天遇到这么一个情况,在Action代码中进行文件下载: ActionForm得到file_id,通过file_id进行数据库查询得到file_name以及服务器硬盘上的file_uri,其中file ...

  10. html 基本标签 ---特殊格式

    <address> </address> 地址 <blockquote> </blockquote> 缩进 <q> </q> 小 ...