当前位置: 首页 > news >正文

网站板块建设的重要性毕业设计网站开发流程图

网站板块建设的重要性,毕业设计网站开发流程图,网络营销网站,公司网站开发策划书目录 一. 前言 二. final 的基础使用 2.1. 修饰类 2.2. 修饰方法 2.2.1. private 方法是隐式的 final 2.2.2. final 方法可以被重载 2.3. 修饰参数 2.4. 修饰变量 2.4.1. static final 2.4.2. blank final 2.4.3. 所有 final 修饰的字段都是编译期常量吗#xff1f…目录 一. 前言 二. final 的基础使用 2.1. 修饰类 2.2. 修饰方法 2.2.1. private 方法是隐式的 final 2.2.2. final 方法可以被重载 2.3. 修饰参数 2.4. 修饰变量 2.4.1. static final 2.4.2. blank final 2.4.3. 所有 final 修饰的字段都是编译期常量吗 三. final 域重排序规则 3.1. final 域为基本类型 3.1.1. 写 final 域重排序规则 3.1.2. 读 final 域重排序规则 3.2. final 域为引用类型 3.2.1. 对 final 修饰的对象的成员域写操作 3.2.2. 对 final 修饰的对象的成员域读操作 3.3. final 重排序的总结 四. final 再深入理解 4.1. final 的实现原理 4.2. 为什么 final 引用不能从构造函数中“溢出” 4.3. 使用 final 的限制条件和局限性 一. 前言 在Java中final 表示“最终的、不可改变的、完结的”它也是一种修饰符可以修饰变量、方法和类。final 修饰变量、方法和类时的意义是不同的但本质是一样的都表示不可改变类似 C#里的 sealed 关键字。final 修饰的变量叫做最终变量也就是常量修饰的方法叫做最终方法修饰的类叫做最终类。 二. final 的基础使用 2.1. 修饰类 当某个类的整体定义为 final 时就表明了你不打算继承该类而且也不允许别人这么做。即这个类是不能有子类的。 注意final 类中的所有方法都隐式为 final因为无法覆盖他们所以在 final 类中给任何方法添加final 关键字是没有任何意义的。 这里顺道说说 final 类型的类如何拓展比如 String 是 final 类型我们想写个 MyString 复用所有String 中方法同时增加一个新的 toMyString() 的方法应该如何做 设计模式中最重要的两种关系一种是继承/实现另外一种是组合关系。所以当遇到不能用继承的final修饰的类应该考虑用组合如下代码大概写个组合实现的意思 /** * 流华追梦 */ class MyString {private String innerString;// ...init other methods// 支持老的方法public int length() {return innerString.length(); // 通过innerString调用老的方法}// 添加新方法public String toMyString() {// ...} } 2.2. 修饰方法 被 final 修饰的方法称为常量方法该方法可以被重载也可以被子类继承但却不能被重写。当一个方法的功能已经可以满足当前要求不需要进行扩展我们就不用任何子类来重写该方法防止该方法的内容被修改。比如 Object 类中就有一个 final 修饰的 getClass() 方法Object 的任何子类都不能重写这个方法。 2.2.1. private 方法是隐式的 final 类中所有 private 方法都隐式地指定为 final由于无法取用 private 方法所以也就不能覆盖它。可以对 private 方法增添 final 关键字但这样做并没有什么好处。看下下面的例子 public class Base {private void test() {} }public class Son extends Base {public void test() {}public static void main(String[] args) {Son son new Son();Base father son;// father.test();} } Base 和 Son 都有方法 test()但是这并不是一种覆盖因为 private 所修饰的方法是隐式的final也就是无法被继承所以更不用说是覆盖了在Son中的 test() 方法不过是属于Son的新成员罢了Son 进行向上转型得到 father但是 father.test() 是不可执行的因为Base中的 test 方法是 private 的无法被访问到。 2.2.2. final 方法可以被重载 我们知道父类的 final 方法是不能够被子类重写的那么final方法可以被重载吗答案是可以的下面代码是正确的 public class FinalExampleParent {public final void test() {}public final void test(String str) {} } 2.3. 修饰参数 在方法参数前面加 final 关键字就是为了防止数据在方法体中被修改。 主要分两种情况第一用 final 修饰基本数据类型第二用 final 修饰引用类型。 第一种情况修饰基本类型非引用类型。这时参数的值在方法体内是不能被修改的即不能被重新赋值。否则编译就通不过。 第二种情况修饰引用类型。这时参数变量所引用的对象是不能被改变的。作为引用的拷贝参数在方法体里面不能再引用新的对象。否则编译通不过。 但是对于引用如果只是修改引用对象成员的值则不会报任何错完全能编译通过。如下代码 public static void valid(final String[] args) {args[0] 5;System.out.println(args); } 2.4. 修饰变量 被 final 修饰的变量一旦被赋值初始化后就不能再被重新赋值。即变量值只能被赋值一次不可被反复修改所以叫做最终变量也叫做常量。 并且我们在定义 final 变量时必须显式地进行初始化指定初始值否则会出现“The blank final field xxx may not have been initialized”的异常提示。变量值的初始化可以在两个地方一是在变量定义处即在 final 变量定义时直接给其赋值二是在构造方法或静态代码块中。这些地方只能选其一不能同时既在定义时赋值又在构造方法或静态代码块中另外赋值。 我们在开发时通常会把 final 修饰符和 static 修饰符一起使用来创建类的常量。 根据修饰变量的作用范围比如在修饰局部变量和成员变量时final 会有不同的特性 1. final 修饰局部变量时在使用之前必须被赋值一次才能使用 2. final 修饰成员变量时如果在声明时没有赋值则叫做“空白 final 变量”空白 final 变量必须在构造方法或静态代码块中进行初始化。 根据修饰变量的数据类型比如在修饰基本类型和引用类型的变量时final 也有不同的特性 1. final修饰基本类型的变量时不能把基本类型的值重新赋值因此基本类型的变量值不能被改变。 2. final修饰引用类型的变量时final 只会保证引用类型的变量所引用的地址不会改变即保证该变量会一直引用同一个对象。因为引用类型的变量保存的仅仅是一个引用地址所以 final 修饰引用类型的变量时该变量会一直引用同一个对象但这个对象本身的成员和数据是完全可以发生改变的。 2.4.1. static final 一个既是 static 又是 final 的字段只占据一段不能改变的存储空间它必须在定义的时候进行赋值否则编译器将不予通过。 import java.util.Random;public class Test {static Random r new Random();final int k r.nextInt(10);static final int k2 r.nextInt(10); public static void main(String[] args) {Test t1 new Test();System.out.println(k t1.k k2 t1.k2);Test t2 new Test();System.out.println(k t2.k k2 t2.k2);} } // 运行结果k2 k27 k8 k27 我们可以发现对于不同的对象k 的值是不同的但是 k2 的值却是相同的这是为什么呢因为static 关键字所修饰的字段并不属于一个对象而是属于这个类的。也可简单的理解为 static final所修饰的字段仅占据内存的一份空间一旦被初始化之后便不会被更改。 2.4.2. blank final Java 允许生成空白 final也就是说被声明为 final 但又没有给出定值的字段但是必须在该字段被使用之前被赋值这增强了final的灵活性。我们有两种选择 1. 在定义处进行赋值这不叫空白final 2. 在构造器中进行赋值保证了该值在被使用前赋值。 public class Test {final int i1 1;final int i2; // 空白finalpublic Test() {i2 1;}public Test(int x) {this.i2 x;} } 可以看到 i2 的赋值更为灵活。但是请注意如果字段由 static 和 final 修饰仅能在声明时赋值或声明后在静态代码块中赋值因为该字段不属于对象属于这个类。 2.4.3. 所有 final 修饰的字段都是编译期常量吗 现在来看编译期常量和非编译期常量如 public class Test {// 编译期常量final int i 1;final static int J 1;final int[] a { 1,2,3,4 };// 非编译期常量Random r new Random();final int k r.nextInt();public static void main(String[] args) {} } k 的值由随机数对象决定所以不是所有 final 修饰的字段都是编译期常量只是 k 的值在被初始化后无法被更改。  三. final 域重排序规则 上面我们聊的 final 使用是属于 Java 基础层面的当理解这些后我们就真的算是掌握了 final吗有考虑过 final 在多线程并发的情况吗在 Java 内存模型中我们知道 Java 内存模型为了能让处理器和编译器底层发挥他们的最大优势对底层的约束就很少也就是说针对底层来说 Java 内存模型就是一弱内存数据模型。同时处理器和编译器为了性能优化会对指令序列有编译器和处理器重排序。那么在多线程情况下final 会进行怎样的重排序会导致线程安全的问题吗下面就来看看 final 的重排序。 3.1. final 域为基本类型 看下面这段示例性代码假设线程A 在执行 writer() 方法线程B 执行 reader() 方法 public class FinalDemo {private int a; // 普通域private final int b; // final域private static FinalDemo finalDemo;public FinalDemo() {a 1; // 1.写普通域b 2; // 2.写final域}public static void writer() {finalDemo new FinalDemo();}public static void reader() {FinalDemo demo finalDemo; // 3.读对象引用int a demo.a; // 4.读普通域int b demo.b; // 5.读final域} } 3.1.1. 写 final 域重排序规则 写 final 域的重排序规则禁止对 final 域的写重排序到构造函数之外这个规则的实现主要包含了两个方面 1. JMM 禁止编译器把 final 域的写重排序到构造函数之外 2. 编译器会在 final 域写之后构造函数 return 之前插入一个 StoreStore 屏障。这个屏障可以禁止处理器把 final 域的写重排序到构造函数之外。 我们再来分析 writer 方法虽然只有一行代码但实际上做了两件事情 1. 构造了一个FinalDemo对象 2. 把这个对象赋值给成员变量 finalDemo。 我们来画下存在的一种可能执行时序图如下 由于 a、b 之间没有数据依赖性普通域普通变量a 可能会被重排序到构造函数之外线程B就有可能读到的是普通变量a 初始化之前的值零值这样就可能出现错误。而 final 域变量b根据重排序规则会禁止 final 修饰的变量b 重排序到构造函数之外从而变量b 能够正确赋值线程B 就能够读到 final 变量初始化后的值。  因此写 final 域的重排序规则可以确保在对象引用为任意线程可见之前对象的 final 域已经被正确初始化过了而普通域就不具有这个保障。比如在上例线程B 有可能就是一个未正确初始化的对象 finalDemo。 3.1.2. 读 final 域重排序规则 读 final 域重排序规则为在一个线程中初次读对象引用和初次读该对象包含的 final 域JMM会禁止这两个操作的重排序。注意这个规则仅仅是针对处理器处理器会在读 final 域操作的前面插入一个 LoadLoad屏障。实际上读对象的引用和读该对象的 final 域存在间接依赖性一般处理器不会重排序这两个操作。但是有一些处理器会重排序因此这条禁止重排序规则就是针对这些处理器而设定的。 read() 方法主要包含了三个操作 1. 初次读引用变量 finalDemo; 2. 初次读引用变量 finalDemo 的普通域 a; 3. 初次读引用变量 finalDemo 的 final 域 b。 假设线程A 写过程没有重排序那么线程A 和线程B 有一种的可能执行时序为下图 读对象的普通域被重排序到了读对象引用的前面就会出现线程B 还未读到对象引用就在读取该对象的普通域变量这显然是错误的操作。而 final 域的读操作就“限定”了在读 final 域变量前已经读到了该对象的引用从而就可以避免这种情况。  读 final 域的重排序规则可以确保在读一个对象的 final 域之前一定会先读包含这个 final 域的对象的引用。 3.2. final 域为引用类型 我们已经知道了 final 域是基本数据类型的时候重排序规则是怎么样的了如果是引用数据类型呢 我们接着继续来探讨。 3.2.1. 对 final 修饰的对象的成员域写操作 针对引用数据类型final 域写针对编译器和处理器重排序增加了这样的约束在构造函数内对一个 final 修饰的对象的成员域的写入与随后在构造函数之外把这个被构造的对象的引用赋给一个引用变量这两个操作是不能被重排序的。注意这里的是“增加”也就说前面对 final 基本数据类型的重排序规则在这里还是使用。这句话是比较拗口的下面结合实例来看 public class FinalReferenceDemo {final int[] arrays;private FinalReferenceDemo finalReferenceDemo;public FinalReferenceDemo() {arrays new int[1]; // 1arrays[0] 1; // 2}public void writerOne() {finalReferenceDemo new FinalReferenceDemo(); // 3}public void writerTwo() {arrays[0] 2; // 4}public void reader() {if (finalReferenceDemo ! null) { // 5int temp finalReferenceDemo.arrays[0]; // 6}} } 针对上面的实例程序线程线程A 执行 wirterOne() 方法执行完后线程B 执行 writerTwo() 方法然后线程C 执行 reader() 方法。下图就以这种执行时序出现的一种情况来讨论耐心看完才有收获。 由于对 final 域的写禁止重排序到构造方法外因此1 和 3 不能被重排序。由于一个 final 域的引用对象的成员域写入不能与随后将这个被构造出来的对象赋给引用变量重排序因此 2 和 3 不能重排序。  3.2.2. 对 final 修饰的对象的成员域读操作 JMM 可以确保线程C 至少能看到写线程A 对 final 引用的对象的成员域的写入即能看下arrays[0] 1而写线程B 对数组元素的写入可能看到可能看不到。JMM 不保证线程B 的写入对线程C 可见线程B 和线程C 之间存在数据竞争此时的结果是不可预知的。如果可见的可使用锁或者 volatile。 3.3. final 重排序的总结 按照 final 修饰的数据类型分类基本数据类型1. final 域写禁止final域写与构造方法重排序即禁止 final 域写重排序到构造方法之外从而保证该对象对所有线程可见时该对象的final域全部已经初始化过。2. final 域读禁止初次读对象的引用与读该对象包含的 final 域的重排序。 引用数据类型额外增加约束禁止在构造函数对一个 final 修饰的对象的成员域的写入与随后将这个被构造的对象的引用赋值给引用变量重排序。 四. final 再深入理解 4.1. final 的实现原理 上面我们提到过写 final 域会要求编译器在 final 域写之后构造函数返回前插入一个StoreStore 屏障。读 final 域的重排序规则会要求编译器在读 final 域的操作前插入一个 LoadLoad屏障。 很有意思的是如果以 X86 处理器为例X86 处理器不会对写-写重排序所以 StoreStore屏障可以省略。由于不会对有间接依赖性的操作重排序所以在 X86 处理器中读 final 域需要的LoadLoad 屏障也会被省略掉。也就是说以 X86 为例的话对 final 域的读/写的内存屏障都会被省略具体是否插入还是得看是什么处理器。 4.2. 为什么 final 引用不能从构造函数中“溢出” 这里还有一个比较有意思的问题上面对 final 域写重排序规则可以确保我们在使用一个对象引用的时候该对象的 final 域已经在构造函数被初始化过了。但是这里其实是有一个前提条件的也就是在构造函数不能让这个被构造的对象被其他线程可见也就是说该对象引用不能在构造函数中“溢出”。以下面的例子来说 public class FinalReferenceEscapeDemo {private final int a;private FinalReferenceEscapeDemo referenceDemo;public FinalReferenceEscapeDemo() {a 1; // 1referenceDemo this; // 2}public void writer() {new FinalReferenceEscapeDemo();}public void reader() {if (referenceDemo ! null) { // 3int temp referenceDemo.a; // 4}} } 可能的执行时序如图所示  假设一个线程A 执行 writer() 方法另一个线程执行 reader() 方法。因为构造函数中操作 1 和 2 之间没有数据依赖性1 和 2 可以重排序先执行了 2这个时候引用对象 referenceDemo 是个没有完全初始化的对象而当线程B 去读取该对象时就会出错。尽管依然满足了 final 域写重排序规则在引用对象对所有线程可见时其 final 域已经完全初始化成功。但是引用对象 this 逸出该代码依然存在线程安全的问题。 4.3. 使用 final 的限制条件和局限性 当声明一个 final 成员时必须在构造函数退出前设置它的值。 public class MyClass {private final int myField 1;public MyClass() {...} }// 或者public class MyClass {private final int myField;public MyClass() {...myField 1;...} } 将指向对象的成员声明为 final 只能将该引用设为不可变的而非所指的对象。 下面的方法仍然可以修改该 list private final List myList new ArrayList(); myList.add(Hello); 声明为 final 可以保证如下操作不合法 final List myList new ArrayList(); myList someOtherList; 如果一个对象将会在多个线程中访问并且你并没有将其成员声明为 final则必须提供其他方式保证线程安全。其他方式可以包括声明成员为 volatile使用 synchronized 或者显式 Lock 控制所有该成员的访问。 再思考一个有趣的现象 byte b1 1; byte b2 3; byte b3 b1 b2; // 当程序执行到这一行的时候会出错因为b1、b2可以自动转换成int类型的变量运算时Java虚拟机对它进行了转换结果导致把一个int赋值给byte-----出错如果对b1 b2加上final就不会出错 final byte b1 1; final byte b2 3; byte b3 b1 b2; // 不会出错相信你看了上面的解释就知道原因了。
http://www.pierceye.com/news/327251/

相关文章:

  • 做民宿的网站wordpress 短信平台
  • 婚恋网站上认识人 带你做原油交易怎么用手机创造网站
  • 网站建设投标书服务方案范本天津北京网站建设公司
  • 网站建设好评公司微企点建站怎么样
  • 某网站开发项目成本估计推广普通话作文500字
  • 制作网站需要哪些工作网站建设佰金手指科杰十三
  • 外贸哪家做网站wordpress excel搜索
  • 苏州做网站推广的英文搜索网站
  • 政务微网站建设方案深圳市易捷网络科技有限公司
  • 云南网站建设哪家好长沙网站建设营销
  • 四川省建设厅注册中心网站网站管理内容
  • 百度提交网站wordpress广告设置
  • 余姚市城乡建设局网站石家庄上门足疗
  • 深圳工程造价建设信息网站php网站建设题目
  • 龙岗网站制作织梦整合wordpress
  • 代做效果图网站哪家好汉中市建设局网站
  • 东阳海天建设集团网站网站蜘蛛爬行统计
  • asp企业网站cms北京大型网站建设公司
  • 网站要多钱杭州排名优化公司电话
  • 怎么在网站中添加百度商桥南京营销网站建设
  • 沈阳火车站wordpress的vieu主题破解版
  • 食品网站建设 网站定制开发微网站建设的第一步是进行首页的设置
  • 一站式装修公司有哪些500人在线网站建设配置
  • 郴州网站制作哪个网站可以做市场调研报告
  • 劲松网站建设公司做运营需要具备什么能力
  • 企业建设网站是网络营销吗17网站一起做网店新塘
  • 电子书籍网站开发重庆网站建设快速建站
  • 广州 企业网站建设公司网页设计模板
  • 长安网站建设制作价格乐清网站
  • 小游戏网站怎么做建站徐州seo代理计费