Java复习2-对象与类
回顾基础知识过程中遇到的感觉需要记录一下的知识点。
封装
我们设计的class应当尽可能的高内聚,体现为封装的程度。一个class的属性应该只能自己修改,其他class都只是与本class沟通,而不应该有能力修改。比较常见的一个问题是Date属性。
业务开发中经常需要设计class的日期属性,比如birthday, createDate等。
public class User { private String name; private Date birth; public Date getBirth() { return this.birth; } }
我经常设计一个entity,填入字段,然后就直接getter, setter出去,尤其使用lombok后,更是连生成都改自动了。按照封装的要求,这样的做法是不合适的。因为其他class可以获取Date对象,Date对象是可变的。那么,就有可能会产生日期被修改的可能。
如果项目有引入Findbugs的扫描,这个class肯定会被扫描出来的,不应该返回一个可变对象。那么,怎么做才可以避免这个问题?我们确实需要暴露Date啊。
答案是暴露一个不可变的对象。Java8推出了新的日期API,其中LocalDate就是不可变的。用LocalDate替换Date即可。LocalDate就像String一样,没有提供任何可以改变内部属性的方法,所有的修改之类的方法都将会创建一个新的对象。这样,修改操作将不会影响原来的class。
Date对象可以解决,但很多是自己定义的对象,这个怎么办?比如,User有个属性是Role。
public class User { private String name; private LocalDate birth; private Role role; public Role getRole() { return this.role; } }
和Date类似,当调用getRole
之后,外界如果可以修改role的属性,比如把role改成admin,那么本对象就拥有了admin权限了。这是我们不愿看到的。可以模仿LocalDate,把Role的所有修改内部属性的方法关闭。这样,外部无法修改Role,就不会影响到User了。然而,我们web中需要把对象渲染成json发送出去,jackson会根据getter setter去做序列化和反序列化操作。这个setter还不能关闭。
那就只能处理User自己了。user唯一发生风险的地方在于getRole后,把自己内部属性暴露出去了。我们可以getRole的时候给一个新的出去,让其他class随便改都不会影响自己。
public class User { private String name; private LocalDate birth; private Role role; public Role getRole() { return (Role) this.role.clone(); } }
现实是,我们很少关注这样的做法,都是直接返回。而且,也很少遇到错误。这时候可以 忽略findbugs的异常。但,最好的,还是推荐做这样的修改。
方法传参的按值调用
初学Java的时候最容易搞不懂的地方就是传递参数到底是怎么传递的。
在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值。而按引用调用(call be reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。按...调用(call by)是一个标准的计算机科学术语,它用来描述各种程序设计语言(不只Java)中方法参数的传递方式。
Java程序设计语言总是按值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。
int a = 10; addOne(a)
不管addOne方法具体实现,a最终依旧还是10. 因为当a传递给addOne方法的时候,拷贝了一份a的值给参数,方法中运行过程中都是一份拷贝,不会影响原来的变量。
方法参数共有两种:
- 基本数据类型(数字、布尔值)
- 对象引用
上述demo显示一个方法不可能修改一个基本数据类型的参数。那么对象引用呢?
StringBuilder sb = new StringBuilder(); appendOne(sb);
appendOne执行过程中会对sb产生影响吗?
这就要看具体方法内容了。比如
public void appendOne(StringBuilder sb) { sb.append("1"); }
那么,我们最终执行完结果肯定sb内容添加了1。而换一个方式,
public void appendOne2(StringBuilder s){ s = new StringBuilder(); s.append("a"); }
这样,方法外面的sb的内容会变成什么?
这个,首先要记住的是Java方法传参都只是传递拷贝。然后,明白传递拷贝的意义
因为s指向的地址和sb相同,故,当s.append的时候,sb的内容也会改变。这也上开头讲述的封装不应返回一个可变变量的原因。任何拿到这个可变变量地址的方法都可以直接修改变量里的属性。那方法2有什么不同?
方法2中,把参数s指向了新地址,那么接下来的任何修改,都将不会影响旧地址。则方法外的sb对应的地址空间也就不会发生变化。这个可以理解为Java传递对象引用的时候只复制了对象引用的地址。
类设计技巧
- 一定要保证数据私有,即封装性;
- 一定要对数据初始化,最好不要依赖系统的默认值,自己给定一个初始值;
- 不要在类中使用过多的基本类型,可以把相关的几个变量合成一个class,转为引用class,另外,能用包装类就不用基本类型;
- 不是所有的成员变量都应该提供对外访问方法,比如创建日期不可以修改;
- 将职责过多的类进行分解;
- 类名和方法名要能够体现他们的职责;
- 优先使用不可变的类。
关注我的公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
第3章—高级装配—运行时注入
运行时注入 当我们经常用如下的硬解码方式来配置文件: <bean id="SgtPeppers" class="com.CDDemo.SgtPeppers" p:title="sgt" p:song="Twinkle, twinkle, little start"> <property name="title" value="sgt"/> <property name="song" value="Twinkle, twinkle, little start"/> </bean> 但有时我们需要避免硬解码,需要想要这些值在运行时确定,Spring提供了两种在运行时求值的方式: 属性占位符 Spring表达式语言(SpEL) 1.注入外部的值 在Spring中,处理外部值得最简单方式就是申明属性源并通过Spring的Enviroment来检索属性.例如: @Configuration @PropertySource("classpath:app.properties") public class ExpressionTest { @Aut...
- 下一篇
Idea(三)常用插件以及快捷键总结
idea常用插件以及快捷键 现在开发中和日常自己开发都统一换成了idea进行开发了。现在针对自己常用到的插件和快捷键进行总结记录下。 插件 Alibaba Java Coding Guidelines:阿里巴巴编码规约 Grep Console:控制台颜色输出日志 快捷键 查找文件:Ctrl + N 查看文件内所有已经声明的方法: Alt + 7 跳转到方法实现处: Ctrl + Alt + B 清除无效 import 的快捷键: ctrl+alt+o 跳转到指定行 :Ctrl + G 查找内容:Ctrl + F 替换内容:Ctrl + R 撤销操作:Ctrl + z 反撤销: Ctrl + Shift + z 本篇博客长期更新,用到自己感觉比较好的就会更新 学习不是要么0分,要么100分的。80分是收获;60分是收获;20分也是收获。有收获最重要。但是因为着眼于自己的不完美,最终放弃了,那就是彻底的0分了。
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS6,CentOS7官方镜像安装Oracle11G
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS8编译安装MySQL8.0.19