阅读视图

发现新文章,点击刷新页面。
🔲 ⭐

还在手写 Getter/Setter 方法吗?Lombok 让你的代码更简洁!

以下是文章大纲:

lombok

Lombok 是什么?

官网:https://projectlombok.org/

Lombok 是一个 Java 库,使用 Lombok 可以通过简单的注解帮助我们消除 Java 的样板代码,使代码更加简洁清晰。

比如对于简单的 Java 对象(POJO),很多的代码里充斥着大量的 getter()setter() 方法,样板代码占用比例高,影响可读性,引入 Lombok 只需一个注解就能达到相同效果,而且更简洁。

Lombok 引入

1、POM 中引入依赖

使用 Lombok 注解需要依赖它的库。

Maven 库:https://mvnrepository.com/artifact/org.projectlombok/lombok

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.16</version>
</dependency>

系统中如果有引入无需重复引入。

2、IDE 中安装插件

为什么装插件?

  • 因为 Lombok 的引入后,java 文件使用 javac 编译成的字节码文件中就会包含 getter 和 setter 函数,但是源代码中找不到定义,IDE 会认为这是错误,因此需要安装一个 Lombok 的插件,方便本地 IDE 的研发工作。

IDEA 中安装:

Settings->Plugins->输入框输入”lombok”

图片

Lombok 使用

比如要针对以下 Java 对象提供 getter()setter() 方法:

public class Mountain {

    private String name;

    private String country;
}

只需要在类上打 @Getter@Setter 注解。

@Getter
@Setter
public class Mountain {

    private String name;

    private String country;
}

然后用 maven 编译一下:mvn clean compile

使用的时候就可以正常拿到像 getCountry()setCountry() 这样的方法。通过 IDE 找方法调用处也可以直接锁定到字段上,以前要点两下才能看到字段明细信息,使用 Lombok 点一下就能达到效果。

图片

Lombok 使用注意

  • 不建议直接使用 @Data 这种大而全的方式覆盖太多的简化场景。
  • toString 场景不建议使用 Lombok 的,建议使用系统自定义 ToString 里的方法。
  • 对外包考虑到接入方也需要引入依赖,如果担心副作用,可以不引入。
  • 其他注解酌情使用。

Lombok 原理

自 JDK6 之后,javac 在执行的时候会调用实现了 JSR 269 API 的程序,这样我们就可以对编译器做一些增强,这时 javac 执行的过程如下:

图片

Lombok 本质上就是一个实现了“JSR 269 API”的程序。在使用 javac 的过程中,它产生作用的具体流程如下:

  1. javac 对源代码进行分析,生成一棵抽象语法树(AST)
  2. 运行过程中调用实现了“JSR 269 API”的 Lombok 程序
  3. Lombok 对第一步骤得到的 AST 进行处理,找到 @Getter @Setter 注解所在类对应的语法树(AST)
  4. Lombok 修改该语法树(AST),增加 getter()setter() 方法定义的相应树节点
  5. javac 使用修改后的抽象语法树(AST)生成字节码文件,即给 class 增加新的节点(代码块)

好了,今天的分享就到这里,我很多项目在工程实践中都引入了 Lombok,对编码效率提升很大,读者朋友可以尝试使用,也可以和我交流心得。如果这篇文章对你有用的话,欢迎分享转发,这会对我有很大的帮助。


推荐阅读:

Java 面向对象一览

Java 核心类库一览

更多内容请收藏:Java for You

🔲 ☆

【Java面试题01】基础知识精选

Java面试

前言

如果你是 Java 后端方向,Java 基础的知识就必须要相当熟悉。

蜗牛也做过面试官,本篇文章就从面试官的角度上,精选出 Java 基础相关的题目,我会给出参考答案,也会指明考察点,欢迎大家一起互动交流。

Java面试题目

1、什么是 JDK?你常用的版本是哪个?为什么用这个版本?

【考察点】

对 Java 开发工具的理解,以及技术选型方面的思考

【参考答案】

JDK 全称 Java Development ToolKit,直译一下就是 Java 语言开发工具包。

JDK 包含了 开发者工具比如 javac 用来编译 java 源码,jar 用来打包,还有一系列其他工具。此外,还包含了 JRE(Java Runtime Environment),也就是 Java 应用程序的运行环境,它除了运行程序的 java 指令外,还有类库以及执行 Java 应用程序的 JVM(Java 虚拟机)。

我常用的版本是 JDK8。一方面是这个版本已经比较稳定,另一方面,这个版本使用非常普遍,出了问题,很容易在网上找到答案。

图片
 

2、一个Java程序从编码到运行,这中间发生了什么?

【考察点】

对 Java 程序运行原理的理解

【参考答案】

  1. 首先编码之后得到 .java 后缀的源代码文件。
  2. 用 JDK 中的 javac 命令将 Java 源代码进行编译,生成 Java 字节码,也就是 class 文件。
  3. 用 JRE 的 java 命令执行 class 文件时,Java 字节码会被传输到 JVM(Java 虚拟机),JVM 会合并字节码以及 JRE 中的库文件一起执行,输出特定硬件平台的机器码,或者说指令集。
  4. 机器码被底层物理硬件平台执行
Java面试
 

3、一个极简可运行的 Java 程序,它的代码有哪些要素?

【考察点】

对 Java 编码规范的理解

【参考答案】

  • 类类型
  • 类名
  • 访问修饰符
  • 成对的花括号
  • main 方法
    • 执行语句
图片
  

4、Java 基本类型里的布尔类型占用多大的内存?

【考察点】

对 Java 基本数据类型的理解

【参考答案】

1 字节或 4 字节。

布尔类型表达是或者否,只有 trueflase 两个值,用关键字 boolean 表示,但 JVM 没有针对 boolean 的字节码指令,因此在虚拟机规范里,boolean 类型在编译后会被 int 代替,占用 4 个字节,如果是 boolean 数组,会被编译成 byte 数组类型,每个 boolean 数组元素占 1 个字节。实际情况就取决于各厂商发布的 JVM 实现了。

5、& 和 && 有什么区别?

【考察点】

对 Java 基本类型运算的理解

【参考答案】

&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。

&:既能当做逻辑与运算符,也能当做按位与运算符。

逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所以通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。

按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。

6、以下代码为什么输出的不是 129?

int highIntValue = 129;
byte lowByteValue = (byte)highIntValue;

System.out.println(lowByteValue);

【考察点】

考察对计算机原码、反码和补码的理解

【参考答案】

这里会输出 -127,而不是 129。

在上面代码中,我们知道,int 类型数据是 32位,byte 类型数据为 8 位,Java 把 int 类型数据转成 byte 类型数据时,实质上是截取 int 后 8 位存到 byte 中。

int 类型的 129 三码(原码、反码和补码)一致,都为:0000 0000 0000 0000 0000 0000 1000 0001。计算机中存的是补码

从 int 转换 byte,截取后 8 位为:1000 0001。得到的数据为依然是补码

负数补码转原码的公式:

  • 负数原码=(补码-1)&&数值位取反

我们按照公式,会发现其原码为:补码(1000 0001)–> 反码(1000 0000)–> 原码(1111 1111)。即 1111 1111就是 (byte)highIntValue 的结果。

转换成十进制就是 lowByteValue=-(64+32+16+8+4+2+1)=-127。

7、new String(“xxx”); 这行代码会产生几个对象?

【考察点】

考察对 Java 字符串存储机制的理解

【参考答案】

一个或两个。如果常量池中原来没有 xxx,就是两个。如果有就是一个。

String 是不可变类,Java 会分配一块常量池。通过以下代码解释下常量池的使用。

String str1 = "蜗牛666";

String str2 = "蜗牛666";

String newStr1 = new String("蜗牛666");
String newStr2 = new String("蜗牛666");

str1 这个变量接收了一个常量,于是存储到常量池中,执行到 str2 的赋值语句时,发现已经有相同的常量了,于是 str2 也指向了 str1 刚才那块内存空间。

newStr1 和 newStr2 是通过 new 语法创建的对象,在创建的过程中,Java 会先去常量池中查找是否已有 蜗牛666 对象,如果没有则在常量池中创建一个 蜗牛666 对象,然后在堆中创建一个 蜗牛666 的拷贝对象。

图片
 

8、Java 的封装特性都体现在哪些地方?

【考察点】

考察对 Java 面向对象的基础知识。

【参考答案】

  1. 通过包(package)的方式,把一个模块封装到一起,并由几个接口开放给使用方。使用方只能看到接口信息,而看不到接口实现。
  2. 通过访问权限控制的方式,实现信息隐藏。
  3. 通过 getter 和 setter 方法,实现了对类成员有条件的读取和修改。

9、谈谈你对双亲委派模式的理解?

【考察点】

考察对 Java 类加载机制的理解。

【参考答案】

双亲委派模式要求除了顶层的引导类加载器外,其余的类加载器都应当有自己的父类加载器

双亲委派的工作原理是,如果一个类收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的引导类加载器。

如果父类加载器可以完成类加载任务,就成功返回,如果父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。

你会发现,Java 类随着它的类加载器具备了带有优先级的层次关系,通过这种层级关系,可以避免类的重复加载,当父类已经加载了该类,就没必要子加载器再加载一次。

因此像 java.lang.String 这种 Java 核心 API,即便你同名,JVM 也会优先加载 rt.jar 里的,因为引导类加载器是最顶级的加载器。这样也避免了 Java 核心 API 被随意替换,保证了安全。

图片

后记

很多问题看似简短,背后能说道的东西其实很多,这就需要日积月累的沉淀,而非一朝一夕之功。面试题精选系列每一篇的题目不会超过 10 个,为的是读者朋友可以很好的消化内容,同时也能够深入了解相关的知识,希望对读者的面试能有帮助!


推荐阅读:

编程的本质是什么?

Java 基本类型的各种运算,你真的了解了么?

Java 面向对象一览

更多内容请收藏:Java for You

🔲 ☆

我是如何放弃 JSP,转向 REST 编程的

记得大学搞编程的时候,比起研究数据结构,做算法题,我更喜欢搞 web 编程。因为做 web 是可以通过浏览器快速看到效果的,可视化的页面也能带给自己满足感。

我画了个图,读者朋友可以感受下,自己作为用户,请求自己代码编出来的页面,岂不是很有成就感?

rest

如果你作为用户来访问互联网资源,那么大概的过程是这样的:你在浏览器是录入  URL 或者点击一个超链接后,浏览器会请求 DNS 服务器解析这个 URL,返回域名映射的IP,然后通过 HTTP 请求这个 IP 对应的 web 资源,如果涉及到一些数据的查询,还会访问数据库服务器获取数据,然后把数据返回,web 服务器将数据和样式处理下,转成 web 资源,然后返回给浏览器,经过浏览器的渲染,你就能看到可视化的页面了。

但那时搞 web 编程还比较麻烦,什么 JSP,ASP,前端代码和后端代码杂糅在一起,就这么你离不开我我离不开你似的在 web 服务器上跑着,代码看上去不清爽,很多业务逻辑也没法被其它站点复用。

图片

假设现在有三个巨头企业,他们分别维护 baitu.com,kk.com,taopao.com 三个站点,这些站点向服务端的交互都是通过 JavaScript 客户端实现的。某一天三家合并了,那就会涉及很多业务互通,比如 baitu.com 站点下想访问 kk.com 的一些数据。

图片

这时候该怎么做呢?过去通用的解法是用 SOAP(Simple Object Access Protocol,简单对象访问协议),这是一种基于 XML 格式以及 HTTP 传输方式的数据交换协议。

图片

前端 JavaScript 是不能直接访问 SOAP 服务的,需要先访问到 baitu.com 对应的网站后台,然后由网站后台去访问 kk 提供的 SOAP 服务(要在之前网站后台直接访问数据库的逻辑上,抽取单独的服务层出来),然后再由 baitu.com 的网站后台对返回的数据进行渲染,将 HTML 等资源信息返回给前端。

那么这时候问题就来了,我在 baitu.com 上的一个前端页面上,一旦想加点 kk 才有的数据,我就必须得改 baitu.com 的网站后台,并且要重新接入 kk 提供的 soap 服务。这显然是一种低效的架构方式,相当影响研发效率。

那么有没有一种方式,我不需要经过 baitu 的网站后台,直接就能访问到 kk 的服务呢?页面上业务逻辑的处理,就不要放网站后台了,在 JavaScript 的客户端直接做掉,通过访问后端的某种服务获得业务处理的结果,然后基于网站后台存放的 HTML 和 CSS 来渲染页面。

图片

就像图示的这样,baitu 的 JavaScript 客户端就做两件事,访问后端服务获取业务逻辑处理的结果数据,将数据以 网站后台存放的 HTML 和 CSS 的要求展示出来。这样看来,网站后台就像个壳,只负责本站的 HTML、CSS 和 JavaScript 等静态资源,相关的业务逻辑处理就交给服务来提供。

这就是前后端分离的思想,前端静态资源和后端动态服务解耦。前端只关心 HTML 等前端代码,不涉及一行后端代码,后端只关心自己提供的服务,不涉及一行前端代码。

在这种思想的指引下,SPA(single page web application,单页面应用)就出现了。SPA 是单个 HTML 页面的 Web 应用程序,它在用户与应用程序交互时由 JavaScript 动态更新页面。其工作原理如图。

图片

浏览器客户端一开始会加载必需的 HTML、CSS 和 JavaScript,之后的所有的操作都在这张页面上完成,由 JavaScript 来控制,通过某种数据格式和服务端产生交互,获取返回结果。

这个时候,客户端就需要服务端提供的业务服务得是一个 API(应用程序访问接口),客户端可以直接发起请求,这时候 REST API 就派上用场了。

什么是 REST 呢?

REST 是 REpresentational State Transfer(表述性状态转移) 的首字母缩写,这名字什么鬼?好难理解的样子,不过它本身就源于国外一个博士的论文,论文嘛,大家都知道的,一般不太好理解。我这里画了个图,通过分拆的方式,帮助大家理解下:

图片

REST 是一种设计思想,它的核心是资源,可以理解成在 REST 的世界里,万物皆资源。

  • REpresentational(表述性):这是个形容词,它想要表达的意思是,资源可以用各种形式来进行表述,无论是 XML、JSON 还是 HTML,只要适合资源使用者,任何形式都可以。
  • State(状态):这是个名词,也是 REST 思想的本质。它告诉开发者,REST 关注的是资源当前的状态,而不是对资源采取的行为。无论资源的形式如何变化,它要表达的内容其实是统一的,该资源存在还是不存在,单个信息还是多个信息,都有哪些属性,这就是资源的状态。
  • Transfer(转移):这是个动词,它指转移资源,以某种表述性形式资源从一个应用转移到另一个应用。转移过程中,资源状态可能会有所变化。

在 REST 中,资源是通过 URL 进行识别和定位的。对资源的操作,是通过 HTTP 方法来定义的。HTTP 方法一般会映射到数据层的 CRUD 动作:

数据层动作 HTTP 方法 描述
Create POST 新建资源
Read GET 获取资源
Update PUT 或 PATCH 更新资源
Delete DELETE 删除资源

此映射不是严格限制,读者朋友可以根据实际情况灵活映射。

比如很多网站会维护用户的个人资料信息,如果用 REST 来设计相关操作的 API,可以这么设计:

操作项 URL HTTP 方法
新增个人资料 http://api.example.com/profile POST
查询个人资料 http://api.example.com/profile GET
修改个人资料 http://api.example.com/profile PUT
删除个人资料 http://api.example.com/profile DELETE

简单讲,REST 就是 URL 定位资源,HTTP 方法操作资源

图片

REST 的出现是对过去编程模式的重大颠覆,除了架构上客户端和服务端的解耦,前后端各司其职,也极大提升了开发团队的研发效率。希望我在编程模式上的变化和思考能对你有所启发。

原创不易,感谢读者朋友给我点赞,在看,评论和转发,也欢迎关注我,我会分享更多优质原创内容。你的每份支持,我都会当成 99 元的赞赏。

我是蜗牛,Java 后端开发,正在互联网上疯狂爬行,欢迎一起来爬,下期见。


推荐阅读:

五分钟快速构建第一个 Spring Boot 应用

Spring Boot 项目的这些文件都是干啥用的?

图片

更多内容请收藏:Java for You

🔲 ⭐

Java 数组迭代你用对了吗?

Java 数组迭代

Java 数组是我们学习或工作中常用到的数据结构,我们会经常写数组迭代的代码,但你的代码是最优雅的么?

本文介绍三种 Java 数组迭代的方式以及最佳实践。

1、概述

首先我们通过遍历数组值的基本方法,来迭代数组。其次我们用 Java 比较古老的方法实现,最后我们再使用 Java 8 的 Stream API 将数组转成迭代器。

除此之外,我们可以把这个技巧应用在字符串的处理上。

2、通过循环进行数组迭代

通过循环在数组上建立迭代逻辑,根据索引从数组中获取相应的值。

代码:

package cn.java4u.codebase.array.iterator;

/**
 * @author 蜗牛
 * @from 公众号:蜗牛互联网
 */

public class ArrayIterate {

    public static void main(String[] args) {

        // string array
        String[] names = new String[] {"john""Amal""Paul"};         // iterating array over its values.         for(int index=0; index< names.length ; index++) {             System.out.println(names[index]);         }     } }

输出:

john
Amal
Paul

3、JDK 8 之前使用老方式进行数组迭代

JDK 8 之前,在数组转列表的时候,按照老方式我们通常使用  Arrays.asList() 方法先得到一个 list,然后使用 list.iterator() 方法得到一个迭代器,最后遍历迭代器里所有的值。

package cn.java4u.codebase.array.iterator;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * @author 蜗牛
 * @from 公众号:蜗牛互联网
 */
public class JavaArrayToIterableExample {

    public static void main(String[] args) {

        // string array
        String[] names = new String[]{"john""Amal""Paul"};

        // string array to list conversion
        List<String> namesList = Arrays.asList(names);

        // List to iterable
        Iterator<String> it = namesList.iterator();

        // printing each value from iterator.
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

输出:

john
Amal
Paul

4、使用 Java 8 Stream 进行数组迭代

上一小节中,我们是通过  Arrays.asList() 把数组转成列表,现在我们使用 Java 8 Stream API 也可以做到,只需要用 Arrays.stream() 替换就行,它接收数组并返回数组类型的数据流。

stream() 方法可以把数组转换为 Stream 对象,在较大的数组上使用并行执行能力,然后需要调用 Stream 对象上的 iterator() 方法将 Stream 转换为 Iterator

参考代码如下:

package cn.java4u.codebase.array.iterator;

import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;

/**
 * @author 蜗牛
 * @from 公众号:蜗牛互联网
 */
public class JavaArrayToIterableExampleJava8 {

    public static void main(String[] args) {

        // string array
        String[] names = new String[] {"john""Amal""Paul"};

        System.out.println("多行打印的解决方案");
        // Convert string array to Stream<String>
        Stream<String> namesList = Arrays.stream(names);

        // Stream to iterable
        Iterator<String> it = namesList.iterator();

        // printing each value from iterator.
        while(it.hasNext()) {
            System.out.println(it.next());
        }

        // singel line
        System.out.println("\n单行打印的解决方案");
        Arrays.stream(names).iterator().forEachRemaining(name -> System.out.println(name));
    }
}

多行和单行解决方案提供了相同的输出。如果你打算在实际项目中使用,最好用单行语句,因为这显得更像专家,并且利用了 Stream 的优势!

多行打印的解决方案
john
Amal
Paul

单行打印的解决方案
john
Amal
Paul

5、字符串的应用

如果你理解了上边的内容,那么在字符串上应用迭代能力就很简单了。

比如我们有个字符串,用空格分隔了不同的内容,假设是:1 2 3 4 5 6,我们需要把每个内容分别打印出来,此时就可以应用 Java 8 Stream 来进行迭代处理。

package cn.java4u.codebase.array.iterator;

import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;

/**
 * @author 蜗牛
 * @from 公众号:蜗牛互联网
 */
public class JavaStringToIterableCase {
    public static void main(String[] args) {

        // string
        String numbers = "1 2 3 4 5 6";

        // string to string array
        String[] numbersArray = numbers.split(" ");

        System.out.println("多行打印的解决方案");
        // Convert string array to Stream<String>
        Stream<String> numbersList = Arrays.stream(numbersArray);

        // Stream to iterable
        Iterator<String> it = numbersList.iterator();

        // printing each value from iterator.
        while(it.hasNext()) {
            System.out.println(it.next());
        }

        // singel line
        System.out.println("\n单行打印的解决方案");
        Arrays.stream(numbersArray).iterator().forEachRemaining(name -> System.out.println(name));
    }
}

输出:

多行打印的解决方案
1
2
3
4
5
6

单行打印的解决方案
1
2
3
4
5
6

6、总结

本文介绍了如何对数组做迭代的三种方法,分别是循环方式、集合迭代器方式以及流迭代器方式,最后把流迭代器方式应用在了字符串的处理上。

推荐阅读:

1120页的Leetcode算法刷题笔记,完整版PDF开放下载!

《Java 工程师成神之路》.pdf

更多内容请收藏:Java for You

❌