final关键字的一些思考

final关键字修饰的变量一定不能修改吗?

在传统的编程思想中,final 关键字修改的变量一旦被赋值,就无法通过正常的代码进行修改

相信很多人都是这么想的,包括笔者。
那有没有可能通过不正常的代码进行修改呢
假设示例类如下。

public class Test {

    private final String s = "aaa";

    @Override
    public String toString() {
        return "Test{" + "s='" + s + '\'' + '}';
    }

}

通过反射修改尝试修改字符串的值。

public class Main {

    public static void main(String[] args) throws Exception {
        Class<Test> clazz = Test.class;
        Test test = clazz.newInstance();
        System.out.println("test = " + test);
        Field field = clazz.getDeclaredField("s");
        field.setAccessible(true);
        field.set(test, "bbb");
        System.out.println("test = " + test);
    }
}

输入结果如下:

修改前 test = Test{s='aaa'}
修改后 test = Test{s='aaa'}

那如果示例类修改一下呢?

public class Test {

    private final String s;

    public Test(String s) {
        this.s = s;
    }

    @Override
    public String toString() {
        return "Test{" + "s='" + s + '\'' + '}';
    }

}

再次尝试通过反射修改字符串的值。

public class Main {

    public static void main(String[] args) throws Exception {
        Class<Test> clazz = Test.class;
        Constructor<Test> constructor = clazz.getConstructor(String.class);
        Test test = constructor.newInstance("aaa");
        System.out.println("test = " + test);
        Field field = clazz.getDeclaredField("s");
        field.setAccessible(true);
        field.set(test, "bbb");
        System.out.println("test = " + test);
    }
}

输出结果如下。

修改前 test = Test{s='aaa'}
修改后 test = Test{s='bbb'}

观察结果发现,通过构造方法向 final 关键字修饰的变量赋值,可以通过反射方法修改;而直接指定的实例变量无法通过反射方法修改。


final 关键字修改的变量一定需要初始化吗?

在传统的的编程思想中, final 关键字修改的变量必须在程序要么赋予常量(静态变量或者实例变量),要么通过构造方法在实例初始化时指定。

我想没有老铁会不认可这个观点吧。
但,真的如此吗?
接下类做个实验。使用 BCEL(Byte Code Enginerring Library) 开源库(JDK自带)尝试生成一个和前面的测试类相似的类,只是不再构造方法中初始化 final 类型的变量,大概效果如下。

package io.github.lamtong.codegen;

public class Test {
    private final String s;

    public Test() {
    }
}

相信读者已经猜到了,这个类不能通过正常代码实例化,毕竟是动态生成 .class 文件并加载,源文件中不存在这个类。因此同样通过反射来创建实例。动态生成字节码文件和测试代码如下。

package io.github.lamtong;

import com.sun.org.apache.bcel.internal.Const;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.generic.*;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

public class Main {

    private static final String CLASS_NAME = "io.github.lamtong.codegen.Test";

    public static void main(String[] args) {
        CustomClassLoader classLoader = new CustomClassLoader();
        byte[] bytes = generateOrdinaryClass();
        try {
            classLoader.setBytes(bytes);
            Class<?> clazz = classLoader.loadClass(CLASS_NAME);

            Constructor<?> constructor = clazz.getConstructor();
            Object o = constructor.newInstance();

            Field field = clazz.getDeclaredField("s");
            int modifiers = field.getModifiers();
            System.out.println("字段是否 private: " + Modifier.isPrivate(modifiers));
            System.out.println("字段是否 final: " + Modifier.isFinal(modifiers));

            field.setAccessible(true);
            System.out.println("调用无参构成方法生成实例, 修改前属性 s = " + field.get(o));
            field.set(o, "aaa");
            System.out.println("调用无参构成方法生成实例, 修改后属性 s = " + field.get(o));
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException |
                 InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings(value = {"all"})
    private static byte[] generateOrdinaryClass() {
        ClassGen classGen = new ClassGen(CLASS_NAME, Object.class.getName(), "<generated>", Const.ACC_PUBLIC | Const.ACC_SUPER, new String[]{});
        ConstantPoolGen constantPool = classGen.getConstantPool();
        classGen.setMinor(0);
        classGen.setMajor(52);

        // 添加实例变量
        FieldGen fieldGen = new FieldGen(Const.ACC_PRIVATE | Const.ACC_FINAL, Type.STRING, "s", constantPool);
        classGen.addField(fieldGen.getField());

//         创建无参构造方法
        InstructionList list = new InstructionList();
        InstructionFactory factory = new InstructionFactory(constantPool);
        MethodGen methodGen = new MethodGen(Const.ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[]{}, "<init>", CLASS_NAME, list, constantPool);
        list.append(new ALOAD(0));
        list.append(factory.createInvoke(Object.class.getName(), "<init>", Type.VOID, Type.NO_ARGS, Const.INVOKESPECIAL));
        InstructionHandle ret = list.append(InstructionConst.RETURN);

        methodGen.setMaxStack();
        classGen.addMethod(methodGen.getMethod());
        list.dispose();

        JavaClass javaClass = classGen.getJavaClass();
        try {
            javaClass.dump("C:\\Users\\lemon\\Desktop\\newProxy\\Test.class");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return javaClass.getBytes();
    }

    private static final class CustomClassLoader extends ClassLoader {

        private byte[] bytes;

        public void setBytes(byte[] bytes) {
            this.bytes = bytes;
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            return defineClass(name, bytes, 0, bytes.length, null);
        }

    }

}

结果如下:

字段是否 private: true
字段是否 final: true
调用无参构成方法生成实例, 修改前属性 s = null
调用无参构成方法生成实例, 修改后属性 s = aaa

意不意外,开不开心?

生成的类允许 final 不被初始化也能正常运行,即不直接指定为常量,也不通过构造方法初始化。

总结如下:

  1. final 关键字修饰的实例变量或者静态变量若直接指定为常量,则通过反射代码无法完成修改。
  2. final 关键字修改的实例变量若是通过构造方法初始化的,则通过反射代码可以完成修改。
  3. final 关键字修改的实例变量不一定需要初始化程序才能运行。对 final 关键字代码进行初始化更多的是编译器的约束和语义的要求。

如有不对,欢迎指正……

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
目 录 第一章 JAVA入门 10 计算机语言发展史 10 机器语言 10 汇编语言 10 高级语言 10 其他高级语言 11 JAVA发展简史 12 JAVA为什么能够流行? 13 JAVA各版本的含义 13 JAVA技术体系架构 14 JAVA的特性和优势 14 JAVA应用程序的运行机制 15 JVM(JAVA VIRTUAL MACHINE) 16 Java运行时环境JRE(Java Runtime Environment) 17 JAVA语言应用范围 18 第一个JAVA程序 18 JAVA开发环境搭建 18 一个典型的JAVA程序的编写和运行过程 19 第一个程序常见错误 20 第一个JAVA程序的总结和提升 20 常用Java开发工具 20 常用dos命令 21 本章笔试作业 21 本章上机操作 21 第二章(1) 编程的基本概念 22 注释 22 标识符 22 关键字/保留字 23 变量(variable) 24 常量(Constant) 25 命名规则(规范) 25 基本数据类型(primitive data type) 26 整型变量 26 浮点型 27 字符型(2个字节): 28 boolean类型 29 运算符(operator) 29 二元运算符 29 一元运算符 30 布尔逻辑表达符 30 位运算符 30 扩展运算符 31 字符串连接符 31 三目条件运算符 31 运算符优先级的问题 31 自动类型转换 32 基本类型转化时常见错误和问题 33 方法 33 简单的键盘输入和输出 33 本章思考作业 34 上机操作 34 第二章(2) 控制语句 35 顺序结构 35 选择结构 35 if单选择结构 35 if-else双选择结构 35 If-elseif-else多选择结构 36 switch多选择结构 37 循环结构 39 While和dowhile的区别 41 For循环 42 break语句和continue语句 47 语句块 48 递归结构 49 本章作业 50 本章上机操作 51 第三章 JAVA面向对象程序开发 52 编程语言发展史 52 类和对象是如何产生发展的?如何进化的? 52 面向对象思想初步(OOP初步Object Oriented Programming) 53 面向对象编程的语言的三大特征简介 56 对象和类的概念 56 类和对象初步 57 测试类的定义方式 57 简单的学生类编写示例 58 内存分析 59 属性(field,或者叫成员变量) 59 引用类型 60 类的方法 60 对象的创建和使用 60 构造器(或者叫做构造方法,constructor) 60 垃圾回收机制(Garbage Collection) 63 方法的重载(overload),构造方法的重载 63 this关键字 65 static 关键字 66 静态初始化块(经常用来初始化类,加载类信息时执行!) 67 package 68 JDK中的主要包 68 import 68 eclipse的使用 69 继承(extend, inheritance) 70 为什么需要继承?继承的作用? 70 继承介绍 70 如何实现继承? 70 继承使用要点 71 Object类 72 toString方法 72 equals方法 73 super关键字 74 方法的重写(override) 74 隐藏/封装(encapsulation) 75 为什么需要封装?封装的作用和含义? 75 使用访问控制符,实现封装 76 封装的使用细节 76 多态(polymorphism) 76 为什么需要多态? 76 如何实现多态? 77 方法绑定(method binding) 77 静态绑定 77 动态绑定 77 多态的使用要点 78 对象的转型(casting) 79 final 81 抽象类 82 抽象类的使用要点 83 接口 83 为什么需要接口? 84 如何定义接口? 84 接口的本质探讨 84 接口使用要点 85 接口的多继承 86 面向接口编程 87 OOP更多应用 87 组合 87 内部类(innerclasses) 88 字符串(java.lang.String类)的使用 90 字符串相等的判断 92 思考作业 93 上机作业 94 第四章 异常机制 95 导引问题 95 异常(Exception)的概念 96 异常分类 96 Error 97 Error和Exception的区别 97 Exception 97 异常的处理办法之一,捕获异常 99 try块 99 catch 99 finally 100 try, catch,finally ,return 执行顺序 100 异常的处理办法之二,声明异常:
引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体CD-ROM 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重新使用接口 1.5.1 改善基础类 1.5.2 等价和类似关系 1.6 多形对象的互换使用 1.6.1 动态绑定 1.6.2 抽象的基础类和接口 1.7 对象的创建和存在时间 1.7.1 集合与继承器 1.7.2 单根结构 1.7.3 集合库与方便使用集合 1.7.4 清除时的困境:由谁负责清除? 1.8 违例控制:解决错误 1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2章 一切都是对象 2.1 用句柄操纵对象 2.2 必须创建所有对象 2.2.1 保存在什么地方 2.2.2 特殊情况:主类型 2.2.3 Java中的数组 2.3 绝对不要清除对象 2.3.1 作用域 2.3.2 对象的作用域 2.4 新建数据类型:类 2.4.1 字段和方法 2.5 方法、自变量和返回值 2.5.1 自变量列表 2.6 构建Java程序 2.6.1 名字的可见性 2.6.2 使用其他组件 2.6.3 static关键字 2.7 我们的第一个Java程序 2.8 注释和嵌入文档 2.8.1 注释文档 2.8.2 具体语法 2.8.3 嵌入HTML 2.8.4 @see:引用其他类 2.8.5 类文档标记 2.8.6 变量文档标记 2.8.7 方法文档标记 2.8.8 文档示例 2.9 编码样式 2.10 总结 2.11 练习 第3章 控制程序流程 3.1 使用Java运算符 3.1.1 优先级 3.1.2 赋值 3.1.3 算术运算符 3.1.4 自动递增和递减 3.1.5 关系运算符 3.1.6 逻辑运算符 3.1.7 按位运算符 3.1.8 移位运算符 3.1.9 三元if-else运算符 3.1.10 逗号运算符 3.1.11 字串运算符+ 3.1.12 运算符常规操作规则 3.1.13 造型运算符 3.1.14 Java没有“sizeof” 3.1.15 复习计算顺序 3.1.16 运算符总结 3.2 执行控制 3.2.1 真和假 3.2.2 if-else 3.2.3 反复 3.2.4 do-while 3.2.5 for 3.2.6 中断和继续 3.2.7 切换 3.3 总结 3.4 练习 第4章 初始化和清除 4.1 由构建器保证初始化 4.2 方法过载 4.2.1 区分过载方法 4.2.2 主类型的过载 4.2.3 返回值过载 4.2.4 默认构建器 4.2.5 this关键字 4.3 清除:收尾和垃圾收集 4.3.1 finalize()用途何在 4.3.2 必须执行清除 4.4 成员初始化 4.4.1 规定初始化 4.4.2 构建器初始化 4.5 数组初始化 4.5.1 多维数组 4.6 总结 4.7 练习 第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 类访问 5.5 总结 5.6 练习 第6章 类再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础类 6.3 合成与继承的结合 6.3.1 确保正确的清除 6.3.2 名字的隐藏 6.4 到底选择合成还是继承 6.5 protected 6.6 递增开发 6.7 上溯造型 6.7.1 何谓“上溯造型”? 6.8 final关键字 6.8.1 final数据 6.8.2 final方法 6.8.3 final类 6.8.4 final的注意事项 6.9 初始化和类装载 6.9.1 继承初始化 6.10 总结 6.11 练习 第7章 多形性 7.1 上溯造型 7.1.1 为什么要上溯造型 7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6 内部类 7.6.1 内部类和上溯造型 7.6.2 方法和作用域中的内部类 7.6.3 链接到外部类 7.6.4 static内部类 7.6.5 引用外部类对象 7.6.6 从内部类继承 7.6.7 内部类可以覆盖吗? 7.6.8 内部类标识符 7.6.9 为什么要用内部类:控制框架 7.7 构建器和多形性 7.7.1 构建器的调用顺序 7.7.2 继承和finalize() 7.7.3 构建器内部的多形性方法的行为 7.8 通过继承进行设计 7.8.1 纯继承与扩展 7.8.2 下溯造型与运行期类型标识 7.9 总结 7.10 练习 第8章 对象的容纳 8.1 数组 8.1.1 数组和第一类对象 8.1.2 数组的返回 8.2 集合 8.2.1 缺点:类型未知 8.3 枚举器(反复器) 8.4 集合的类型 8.4.1 Vector 8.4.2 BitSet 8.4.3 Stack 8.4.4 Hashtable 8.4.5 再论枚举器 8.5 排序 8.6 通用集合库 8.7 新集合 8.7.1 使用Collections 8.7.2 使用Lists 8.7.3 使用Sets 8.7.4 使用Maps 8.7.5 决定实施方案 8.7.6 未支持的操作 8.7.7 排序和搜索 8.7.8 实用工具 8.8 总结 8.9 练习 第9章 违例差错控制 9.1 基本违例 9.1.1 违例自变量 9.2 违例的捕获 9.2.1 try块 9.2.2 违例控制器 9.2.3 违例规范 9.2.4 捕获所有违例 9.2.5 重新“掷”出违例 9.3 标准Java违例 9.3.1 RuntimeException的特殊情况 9.4 创建自己的违例 9.5 违例的限制 9.6 用finally清除 9.6.1 用finally做什么 9.6.2 缺点:丢失的违例 9.7 构建器 9.8 违例匹配 9.8.1 违例准则 9.9 总结 9.10 练习 第10章 Java IO系统 10.1 输入和输出 10.1.1 InputStream的类型 10.1.2 OutputStream的类型 10.2 增添属性和有用的接口 10.2.1 通过FilterInputStream从InputStream里读入数据 10.2.2 通过FilterOutputStream向OutputStream里写入数据 10.3 本身的缺陷:RandomAccessFile 10.4 File类 10.4.1 目录列表器 10.4.2 检查与创建目录 10.5 IO流的典型应用 10.5.1 输入流 10.5.2 输出流 10.5.3 快捷文件处理 10.5.4 从标准输入中读取数据 10.5.5 管道数据流 10.6 StreamTokenizer 10.6.1 StringTokenizer 10.7 Java 1.1的IO流 10.7.1 数据的发起与接收 10.7.2 修改数据流的行为 10.7.3 未改变的类 10.7.4 一个例子 10.7.5 重定向标准IO 10.8 压缩 10.8.1 用GZIP进行简单压缩 10.8.2 用Zip进行多文件保存 10.8.3 Java归档(jar)实用程序 10.9 对象串联 10.9.1 寻找类 10.9.2 序列化的控制 10.9.3 利用“持久性” 10.10 总结 10.11 练习 第11章 运行期类型鉴定 11.1 对RTTI的需要 11.1.1 Class对象 11.1.2 造型前的检查 11.2 RTTI语法 11.3 反射:运行期类信息 11.3.1 一个类方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 12.2.4 成功的克隆 12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读类 12.4.1 创建只读类 12.4.2 “一成不变”的弊端 12.4.3 不变字串 12.4.4 String和StringBuffer类 12.4.5 字串的特殊性 12.5 总结 12.6 练习 第13章 创建窗口和程序片 13.1 为何要用AWT? 13.2 基本程序片 13.2.1 程序片的测试 13.2.2 一个更图形化的例子 13.2.3 框架方法的演示 13.3 制作按钮 13.4 捕获事件 13.5 文本字段 13.6 文本区域 13.7 标签 13.8 复选框 13.9 单选钮 13.10 下拉列表 13.11 列表框 13.11.1 handleEvent() 13.12 布局的控制 13.12.1 FlowLayout 13.12.2 BorderLayout 13.12.3 GridLayout 13.12.4 CardLayout 13.12.5 GridBagLayout 13.13 action的替用品 13.14 程序片的局限 13.14.1 程序片的优点 13.15 视窗化应用 13.15.1 菜单 13.15.2 对话框 13.16 新型AWT 13.16.1 新的事件模型 13.16.2 事件和接收者类型 13.16.3 用Java 1.1 AWT制作窗口和程序片 13.16.4 再探早期示例 13.16.5 动态绑定事件 13.16.6 将商业逻辑与UI逻辑区分开 13.16.7 推荐编码方法 13.17 Java 1.1 UI API 13.17.1 桌面颜色 13.17.2 打印 13.17.3 剪贴板 13.18 可视编程和Beans 13.18.1 什么是Bean 13.18.2 用Introspector提取BeanInfo 13.18.3 一个更复杂的Bean 13.18.4 Bean的封装 13.18.5 更复杂的Bean支持 13.18.6 Bean更多的知识 13.19 Swing入门 13.19.1 Swing有哪些优点 13.19.2 方便的转换 13.19.3 显示框架 13.19.4 工具提示 13.19.5 边框 13.19.6 按钮 13.19.7 按钮组 13.19.8 图标 13.19.9 菜单 13.19.10 弹出式菜单 13.19.11 列表框和组合框 13.19.12 滑杆和进度指示条 13.19.13 树 13.19.14 表格 13.19.15 卡片式对话框 13.19.16 Swing消息框 13.19.17 Swing更多的知识 13.20 总结 13.21 练习 第14章 多线程 14.1 反应灵敏的用户界面 14.1.1 从线程继承 14.1.2 针对用户界面的多线程 14.1.3 用主类合并线程 14.1.4 制作多个线程 14.1.5 Daemon线程 14.2 共享有限的资源 14.2.1 资源访问的错误方法 14.2.2 Java如何共享资源 14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 15.1.2 端口:机器内独一无二的场所 15.2 套接字 15.2.1 一个简单的服务器和客户机程序 15.3 服务多个客户 15.4 数据报 15.5 一个Web应用 15.5.1 服务器应用 15.5.2 NameSender程序片 15.5.3 15.5.3 要注意的问题 15.6 Java与CGI的沟通 15.6.1 CGI数据的编码 15.6.2 程序片 15.6.3 用C++写的CGI程序 15.6.4 POST的概念 15.7 用JDBC连接数据库 15.7.1 获得学习示例 15.7.2 查找程序的GUI版本 15.7.3 JDBC API为何如何复杂 15.8 远程方法 15.8.1 远程接口概念 15.8.2 远程接口的实施 15.8.3 创建根与干 15.8.4 使用远程对象 15.8.5 RMI的替选方案 15.9 总结 15.10 练习 第16章 设计范式 16.1 范式的概念 16.1.1 单子 16.1.2 范式分类 16.2 观察器范式 16.3 模拟垃圾回收站 16.4 改进设计 16.4.1 “制作更多的对象” 16.4.2 用于原型创建的一个范式 16.5 抽象的应用 16.6 多重派遣 16.6.1 实现双重派遣 16.7 访问器范式 16.8 RTTI有害吗 16.9 总结 16.10 练习 第17章 项目 17.1 文字处理 17.1.1 提取代码列表 17.1.2 检查大小写样式 17.2 方法查找工具 17.3 复杂性理论 17.4 总结 17.5 练习 附录A 使用非Java代码 A.1 Java固有接口 A.1.1 调用固有方法 A.1.2 访问JNI函数:JNIEnv自变量 A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 其他J/Direct特性 A.4 本原接口(RNI) A.4.1 RNI总结 A.5 Java/COM集成 A.5.1 COM基础 A.5.2 MS Java/COM集成 A.5.3 用Java设计COM服务器 A.5.4 用Java设计COM客户 A.5.5 ActiveX/Beans集成 A.5.6 固有方法与程序片的注意事项 A.6 CORBA A.6.1 CORBA基础 A.6.2 一个例子 A.6.3 Java程序片和CORBA A.6.4 比较CORBA与RMI A.7 总结 附录B 对比C++和Java 附录C Java编程规则 附录D 性能 D.1 基本方法 D.2 寻找瓶颈 D.2.1 安插自己的测试代码 D.2.2 JDK性能评测[2] D.2.3 特殊工具 D.2.4 性能评测的技巧 D.3 提速方法 D.3.1 常规手段 D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物
模块2 Java语言基础 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第1页。 学习目标 01 掌握标识符、关键字、分隔符、变量和常量的含义和使用方法。 02 了解Java注释的格式和代码书写风格。 03 掌握基本数据类型及数据类型转换。 05 掌握从控制台获取用户键盘输入数据的方法。 04 掌握运算符的分类和使用,以及表达式中运算符的优先级和结合性。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第2页。 技能目标 能够在MyEclipse IDE中定义标识符、变量和常量;使用关键字及分隔符。 01 能够在MyEclipse IDE中编写适当的Java注释。 02 能够在MyEclipse IDE中运用基本数据类型并进行数据类型转换。 03 能够在MyEclipse IDE中编写表达式并进行各类运算。 04 能够在MyEclipse IDE中编写程序从控制台获取用户键盘输入。 05 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第3页。 2.1 回顾与思考 【例2-1】编写一个Java应用程序,根据所给圆的半径,计算并输出圆的周长。文件名为Example2_1.java,其代码如下。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第4页。 2.1 回顾与思考 【例2-1】编写一个Java应用程序,根据所给圆的半径,计算并输出圆的周长。 以上代码中,Example2_1、main、args、PI、radius和 perimeter都是标识符;public、class、static、void、final、double和int都是关键字;"{}、()、[]、;"和"."都是分隔符;radius和 perimeter是变量;PI是常量;double和int属于基本数据类型;PI*radius涉及数据类型转换;=、*和+都是运算符;""是字符串定义符;+也是字符串运算符;/**至*/之间、/*至*/之间、//之后的同一行的内容为注释。寥寥几行代码就涉及了Java中很多基本语法。接下来将逐个分析Java语言中的各类基本语法要素。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第5页。 2.2 变量和常量 2.2.1 标识符 标识符用来表示变量、常量、类、方法、参数、接口、包、文件等元素的名字。Java语言中的标识符由字母、数字、下画线和美元符号($)组成,并且需要遵守以下规则。 (1)不能以数字开头。 (2)区分大小写。 (3)没有长度限制。 (4)不能使用Java语言中的关键字。 标识符命名惯例上(但不强迫)遵循见名知义原则和驼峰命名法。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第6页。 2.2 变量和常量 2.2.2 关键字 关键字是Java语言中已被赋予特定含义的标识符,只能供Java编译系统使用。Java语言中不允许用户对关键字再赋予其他含义。Java语言中的关键字见表2-1。 表2-1 Java语言中的关键字 另外,还有两个特殊标识符goto和const。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第7页。 2.2 变量和常量 2.2.3 分隔符 (1) 大括号({}):用来定义语句块、类、方法及局部范围;也用于以赋值方式初始化数组。 (2) 方括号([]):用来声明数组和引用数组元素。 (3) 圆括号(()):用来容纳方法的参数列表;也用于由控制语句和强制类型转换组成的表达式;还用来表示执行或计算的优先级。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第8页。 2.2 变量和常量 2.2.3 分隔符 (4) 分号(;):用来终止一条语句;也用来分隔for循环控制语句圆括号中的表达式。 (5) 逗号(,):用来分隔变量列表中的各个变量或参数列表中的各个参数;也用来分隔for循环控制语句圆括号中各表达式部分的语句序列。 (6) 圆点(.):用来分隔包与其子包或类;也用于类和实例对象调用成员变量和成员方法。 (7) 空白( ):这类分隔符比较特殊,包括空格(Space)、跳格(Tab)和换行(Enter)。 《Java程序设计案例教程》教学课件02Java语言基础全文共48页,当前为第9页。 2.2 变量和常量 2.2.4 变量 变量是程序运行过程中值可以发生改变的量。Java中的变量是程序在计算机内存中的一个基本存储单元。Java中的变量必须先声明后使用。 声明变量的格式为: 变量名必须是Java中合法的标识符,可以依据个人的喜好命名变量。习惯上以其所代表的含义给变量命名,如n

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值