6.8号答辩完就准备找工作,中间与同学聚会、班级照相也用了将近五天时间。6.13号自己开始在BOSS直聘上投简历,也收到了不少公司的面试邀请。为了能多参加几个公司,自己将面试时间约在了端午节之后,也就是从6.19号开始面试,自己约了其中几个自己觉得可以的公司去面试,同时找了自己的小伙伴龙哥一同去面试。这也是自己第一次以社招的身份去北京参加面试。。。希望不会让自己失望。
- 2018.06.19
一天的面试结束了,一天来自己面试了两家公司,下面分别做记录。
上午面试的神州泰岳:
面试官挺好的,上来就请我和龙哥和咖啡,而且聊的很自然,就是感觉面试官技术不是太硬,问的都是基础的问题。第一次以社招身份参加面试,在自我介绍的时候都不知道怎么介绍了。。。。。下面是和面试官聊的技术问题:
1.oracle实现分页。。。。围绕rownum解释
2.shiro简要介绍一下。。。
3.shiro的权重是什么意思?(这个没答上来,到现在都没理解这个问题。。。。。)
4.linux如何开启服务?围绕service和systemctl展开解释
5.tomcat调优。。。
6.任务调度quartz?围绕Timer和quartz简单阐述。。。。。
7.C/S和B/S架构的区别?(狗屎问题。。。。。)
8.redis缓存简要介绍?
9.nginx端口转发和集群? 端口转发:https://www.cnblogs.com/qlqwjy/p/9206937.html 集群:http://www.cnblogs.com/qlqwjy/p/8535488.html
.......
上午没有问啥太难的技术问题,感觉都是一些很浅的问题,最后说如果一面过了会有二面,结果二面还没来。。。。。
下午面试的中科软,
上来先做了一份面试题,题的大概内容是:SQL、java多态、递归、数组。。。。
笔试完是一次技术面试,技术官上来就鄙视应届生,这让我一下没有继续聊下去的欲望,还是跟他聊了点技术问题,在中科软的技术面试还是收获不少的,尤其是二面的时候面试官给自己细细分析了笔试题上的错题,也给自己提了好多建议。最后一面面试官对自己说了他们目前使用的技术,不得不说也挺厉害,使用了当下流行的SpringBoot、activemq 、kafka、docker、webservice(用的居多)等技术。虽然最后说给发offer,但还是不太想去,与心中理想的公司还是有点差距。。。
一面的题:
1.Java与C++的不同:
2.Java特性
3.Java面向对象的特性,,,
在这里在说到多态的时候二面面试官拿着笔试题给自己讲了好多。大概总结为:覆盖就是父类的变量指向子类的对象,调用变量的方法实际上是调用子类的覆盖了父类的方法,这也是多态的体现(面试官说重写是多态,重载是不是还在研究中。。。。。。。)。
Parent p1 = new Child1();
Parent p2 = new Child2();
p1.method1();//调用Child1类重写了父类的method1方法
p2.method1();//调用Child2类重写了父类的method1方法
多态的理解:
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,
多态的实现方式:
方式一:重写:
方式二:接口
方式三:抽象类和抽象方法
4.递归:
两个重要思想:有出口、自己调用自己。
5 SQL聚集函数为什么不能用where?
我的回答是where作用于查询之前,而having作用于查询之后,而聚集函数是对查询完的结果进行整合,所以只能用having
面试官的答案:where是对基本的select进行过滤,而聚集函数本身是将多条记录处理成一条数据,如果使用where本身就是矛盾的。。。。。。。
6.Spring两个重要概念?
IOC和AOP。。。接着对IOC进行了简要概述
最后被问到为什么我们要将对象讲给Spring利用IOC进行管理?为什么?(留给自己慢慢思考。。。。)
网上总结大概是为了符合开闭原则。。。。。。参考:http://weixiaolu.iteye.com/blog/1244865
7.对SpringBoot进行简要概述?.....
8.Mysql与Oracle的区别?(当时没答上来。。。。)
参考:https://www.cnblogs.com/zhouchaoyi/articles/1727678.html
9.你觉得你和那些培训了四个月的人相比,你上了四年的大学,你的优势在哪里?
这个问题我陈思了很久都不知道怎么回答,我突然觉得自己学了四年和别人培训四个月相比还不如别人培训四个月。。。。
面试官给自己的答案:你学了四年培养了你的逻辑能力,所有的问题都会划分为对小模块的增删改查,也就是你与别人相比,可能你的逻辑思维能力比别人更强,化解一个问题的时候更加的得心应手。
附上递归题:
题目:猴子第一天摘了若干个桃子,当即吃了一半,还不解馋,又多吃了一个;第二天,吃剩下的桃子的一半,还不过瘾,又多吃了一个;以后每天都吃前一天剩下的一半多一个,到第10天想再吃时,只剩下一个桃子了。问第一天共摘了多少个桃子?
public class Recursion {
/**
* 求上一天的剩余桃子树 第二天=第一天/2-1 => 第一天=(第二天+1)*2
*
* @param day
* 天数
* @param reserve
* 剩余桃子数
* @return
*/
public static int getTotal(int day, int reserve) {
if (day == 1) {
return reserve;
}
return getTotal(day - 1, (reserve + 1) * 2);
}
public static void main(String[] args) {
System.out.println(getTotal(10, 1));
}
}
网上也有传一个参数的,在这里我直接两个参数。。。。。。
- 2018.06.20
上午面试了天辰信科技有限公司。
感觉工作环境不咋地,就是工作待遇不错,面试的中午还请吃了午饭,创业公司吧可以理解为。。。。。面试题还是有点水平的,面试官还好,对Java基础还是知道不少的,可能对其他技术就不太熟悉了。最终公司也给了offer,工资待遇还算不错,就是工作环境和技术不是自己想要接触的。
一道面试题面试了java的基本数据类型转换,记住一条大的不能给小的。。。。前提得记住8种基本数据类型的大小。byte-1,short-2,int-4,long-8,char-2,boolean-1,faloat-4,double-8 (char型可以转换成int型,我一直以为不能。)
char c='1';
int a=c;//49
补充:float,long,double都可以强转为int,int/int还是int,int/float、double还是float和double,int/long是int
int intNum = 4;
int intNum2 = 3;
float floatNum = 1.6f;
double dounleNum = 2.5;
long longNum = 3l;
System.out.println((int) floatNum);//1
System.out.println((int) dounleNum);//2
System.out.println((int) longNum);//3
System.out.println("=================================");
System.out.println((intNum * floatNum));//6.4
System.out.println((intNum * dounleNum));//10.0
System.out.println((intNum * longNum));//12
System.out.println((intNum / intNum2));//1
System.out.println((intNum / floatNum));//2.5
System.out.println((intNum / dounleNum));//1.6
System.out.println((intNum / longNum));//1
还有一道面试题,是计算 float f=10/4。结果是2.0,计算的时候先计算10/4,结果是2,然后转为float,结果为2.0
还有一道面试题,是 switch可以作用的类型,自己理解了错了,在龙哥的讲解下明白了。记住一条,可以转为int型的都可以作为switch case的数据类型,switch表达式后面的数据类型只能是byte(1),short(2),char(2),int(4)四种整形类型(这些基本类型的包装类型是不支持的),枚举类型和java.lang.String类型(从java 7才允许),不能是boolean类型。
还有一道面试题,是关于float和double类型的题,默认是double类型,所以在使用float的时候需要强转,或者声明为float。
float ff = 9.0f;//不写f编译错误
float ff1 = (float) 9.0;//不强转编译错误
double d = 9.99;//默认是double类型
还有一道关于先用后减和先减后用的题,明白--在前是先减后用,--在后是先用后减。。。。。。
int x=5,x1=5;
int y=x--,y1=--x1;
System.out.println(y);//5
System.out.println(y1);//4
linux的shell类型,这个自己也只是知道bash,记录如下:
sh(全称 Bourne Shell): 是UNIX最初使用的 shell,而且在每种 UNIX 上都可以使用。
Bourne Shell 在 shell 编程方面相当优秀,但在处理与用户的交互方面做得不如其他几种 shell。
bash(全称 Bourne Again Shell): LinuxOS 默认的,它是 Bourne Shell 的扩展。
与 Bourne Shell 完全兼容,并且在 Bourne Shell 的基础上增加了很多特性。可以提供命令补全,命令编辑和命令历史等功能。它还包含了很多 C Shell 和 Korn Shell 中的优点,有灵活和强大的编辑接口,同时又很友好的用户界面。
csh(全称 C Shell): 是一种比 Bourne Shell更适合的变种 Shell,它的语法与 C 语言很相似。
Tcsh: 是 Linux 提供的 C Shell 的一个扩展版本。
Tcsh 包括命令行编辑,可编程单词补全,拼写校正,历史命令替换,作业控制和类似 C 语言的语法,他不仅和 Bash Shell 提示符兼容,而且还提供比 Bash Shell 更多的提示符参数。
ksh (全称 Korn Shell): 集合了 C Shell 和 Bourne Shell 的优点并且和 Bourne Shell 完全兼容。
pdksh: 是 Linux 系统提供的 ksh 的扩展。
linux开放端口,自己写了一长串好像有点问题:参考:https://www.cnblogs.com/qlqwjy/p/8649695.html
/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT
下午面试了中科软:
其实刚进去看环境还算不错,跟自己理想的工作环境也相像。。。。。工作氛围也算一样。做笔试题的时候感觉不像是在招开发人员,7.8个都是对String处理的题。技术面试的时候更是恶心,一个挺狂的技术官说话满不在乎,问了几个Java基础,后来问了一点项目,一点框架的没问,后来问到SQL,第一个SQL就产生了歧义(后来证实是自己错了)。。。后来也就没继续下去了,也不想进行下去了。
SQL大概是:求不及格科目数超过两门的总人数。表结构如下:

数据如下:

自己原来的SQL如下:(查出来发现结果是2:原因是经过count(name)会统计两次张三,只是因为根据name分组,所以只会显示一条记录。)
SELECT
COUNT(NAME)
FROM grade
WHERE score < 60
GROUP BY NAME
HAVING COUNT(score) > 1
改进:在上面的基础上加上distinct关键字即可:(distinct会去掉重复的数据,只统计一条,所以变为最终的结果)
SELECT
COUNT(DISTINCT NAME)
FROM grade
WHERE score < 60
GROUP BY NAME
HAVING COUNT(score) > 1
或者按照常规思路写SQL:
SELECT
COUNT(NAME)
FROM (SELECT
NAME,
COUNT(score) AS menshu
FROM grade
WHERE score < 60
GROUP BY NAME) AS ttt
WHERE ttt.menshu >= 2
总结:分组是对查出的数据进行处理,对分组字段值相同的进行处理,处理成一条记录,因此分组之后的聚集函数都是针对同组的元素进行处理。
给出一个需求要先理清SQL的需求,然后按自己的思路按层次编写SQL。
- 2018.06.22
上午面试的致得软件,刚到公司感觉工作环境是这几天面试的最好的,至少是自己想要的办公桌和办公地点,最终该公司给了offer,我也非常喜欢该公司的工作环境和氛围,所以决定推掉其他的offer,准备入职这家公司。上来先是笔试,然后是机试,然后是面试。笔试题没啥好说的,都是基础的,JSP考察的偏多一些,linux完全没有涉及。完了是机试,机试也是一个递归,一个纯手工的查询sql的题:
1.递归遍历一个文件夹下所有的文件:(例如遍历:F:\bat文件夹下面的)
import java.io.File;
/**
* 递归遍历文件夹,列出所有的文件与子文件
*
* @author: qlq
* @date : 2018年6月21日下午10:45:33
*/
public class First {
public static void main(String[] args) {
listFile(new File("F:/bat"));
}
public static void listFile(File f) {
if (f.isDirectory()) {// 如果是文件夾就打印文件夾名字并递归此文件夹下文件
System.out.println(f.getName());
for (File file : f.listFiles()) {
listFile(file);//递归
}
} else {// 如果是文件直接打印
System.out.println(f.getName());
}
}
}
2.原生的获取JDBC连接并查询数据库并打印查询结果:
public static Connection getConnection() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/web08";
return DriverManager.getConnection(url, "root", "root");
}
@Test
public void query() throws Exception {
Connection con = getConnection();
Statement stmt = con.createStatement();
String sql = "select * from user";
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
System.out.println(username + ", " + password);
}
}
需要注意的是JDBC的获取下标从1开始,好像Java中就JDBC下标从1开始
总结:1.加载驱动直接使用显示加载;
2.获取connection用DriverManager获取连接
3.用conn创建Statement对象
4.Statement执行sql,用resultSet接收结果
5.re.next是将获取的结果游标移到下一行,也就是遍历返回的一行一行的数据。(注意rs.next())
6.关闭对象
晚上我跟龙哥吃饭之后回顾了一年来的项目,聊了很久,也聊了很多有的没的东西。非常想继续与他一起工作,不会的互相研究。一年的合作伙伴,一年来我们相互学习,进步了好多。
- 2018.07.02
早上成功入职致得软件。在填了一些基本信息之后成功入职致得软件公司。上午师傅交给一个任务,说是一天内完成即可。在下午的时候我基本完成了,师傅给我看的时候还是细心的给我纠正问题,比如日志记录用self4j而不用log4j,比如Long比较的时候直接==比较的是内存地址。还比如一些代码规范。。。。。第一天算是通过考核把。。。。
代码如下:
package test2;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DiffUtil {
private static Logger logger = LoggerFactory.getLogger(DiffUtil.class);// slf4j的日志记录器
/**
* 对比文件
*
* @param oldDir
* 旧版本文件(需求中的A文件夹)
* @param nowDir
* 新版本文件(需求中的B文件夹)
* @param diffDir
* 生成对比结果的文件夹(需求中的change文件夹)
*/
public static void compareFile(String oldDir, String nowDir, String diffDir) {
long startTime = System.currentTimeMillis();// 开始时间
// 1.在change文件夹下面生成一个当前日期格式的文件夹
String currentTime = convertCurrentTime2String();
String fileAndLogDir = diffDir + "\\" + currentTime;// 存放日志和更新后的文件的目录
File fileDiffDir = new File(fileAndLogDir);
fileDiffDir.mkdirs();
// 2.获取旧版本文件夹下和新版本文件夹下面的文件的CRC校验码
Map<String, Long> oldFileCRCs = getAllFileNameAndCRC(oldDir, oldDir,
new HashMap<String, Long>());
Map<String, Long> nowFileCRCs = getAllFileNameAndCRC(nowDir, nowDir,
new HashMap<String, Long>());
// 3.遍历删除的文件且将日志信息输出到deleteFile.log
String deleteLogName = "deleteFile.log";
File deleteLogFile = new File(fileDiffDir, deleteLogName);
// 3.1遍历旧文件夹下面的map的key,如果在新文件夹的map中找不到匹配的key值,证明是删除文件了
logger.info("----开始记录删除日志:" + convertCurrentTime2String() + "----");
try {
FileUtils.write(deleteLogFile, "-----开始记录删除日志:"
+ convertCurrentTime2String() + "----\r\n", "UTF-8", true);
} catch (IOException e) {
logger.error("将删除日志写入文件deteFile.log出错", e);
}
List<String> deleteFileNames = new ArrayList<String>();
for (String oldKey : oldFileCRCs.keySet()) {
if (!nowFileCRCs.containsKey(oldKey)) {
logger.info("删除文件\t" + oldKey);
try {
FileUtils.write(deleteLogFile, "删除文件\t" + oldKey + "\r\n",
"UTF-8", true);
} catch (IOException e) {
logger.error("将删除日志写入文件deteFile.log出错", e);
}
deleteFileNames.add(oldKey);
}
}
try {
FileUtils.write(deleteLogFile, "\r\n", "UTF-8", true);
FileUtils.write(deleteLogFile, "---------删除文件日志结束:共删除"
+ deleteFileNames.size() + "个文件----" + "\r\n", "UTF-8",
true);
} catch (IOException e) {
logger.error("将删除日志的统计信息写入文件deteFile.log出错", e);
}
logger.info("-----删除文件日志结束:共删除" + deleteFileNames.size() + "个文件----");
// 4.遍历增加和更新的文件
String addAndUpdateLogName = "addAndUpdate.log";
File addUpdateLogFile = new File(fileDiffDir, addAndUpdateLogName);
logger.info("-----开始记录增加、更新日志------");
List<String> addFileNames = new ArrayList<String>();// 增加文件名字集合
List<String> updateFileNames = new ArrayList<String>();// 更新文件名字集合
for (String nowKey : nowFileCRCs.keySet()) {
if (!oldFileCRCs.containsKey(nowKey)) {
addFileNames.add(nowKey);
} else {
if (oldFileCRCs.get(nowKey).equals(nowFileCRCs.get(nowKey))) {
continue;
}
updateFileNames.add(nowKey);
}
}
// 4.1新增文件写入日志
try {
FileUtils.write(addUpdateLogFile, "-----Diff时间:"
+ convertCurrentTime2String() + "----" + "\r\n", "UTF-8",
true);
FileUtils.write(addUpdateLogFile, "\r\n", "UTF-8", true);
FileUtils.write(addUpdateLogFile, "----共新增文件" + addFileNames.size()
+ "个----\r\n", "UTF-8", true);
logger.info("----共新增文件" + addFileNames.size() + "个----");
} catch (IOException e1) {
logger.error("将新增信息写入文件addAndUpdate.log出错", e1);
}
for (String addFileName : addFileNames) {
try {
logger.info("增加了文件" + addFileName);
FileUtils.write(addUpdateLogFile, "增加了文件" + addFileName
+ "\r\n", "UTF-8", true);
} catch (IOException e) {
logger.error("将新增信息写入文件addAndUpdate.log出错", e);
}
}
// 4.2更新信息写入日志
try {
FileUtils.write(addUpdateLogFile, "\r\n", "UTF-8", true);
FileUtils.write(addUpdateLogFile,
"----共更新文件" + updateFileNames.size() + "个----\r\n",
"UTF-8", true);
logger.info("----共更新文件" + updateFileNames.size() + "个----");
} catch (IOException e) {
logger.error("将更新信息写入文件addAndUpdate.log出错", e);
}
for (String updateFileName : updateFileNames) {
try {
FileUtils.write(addUpdateLogFile, "更新了文件" + updateFileName
+ "\r\n", "UTF-8", true);
logger.info("更新了文件" + updateFileName);
} catch (IOException e) {
logger.error("将更新信息写入文件addAndUpdate.log出错", e);
}
}
// 5.将有新增/更新的文件放入第三个目录中(文件拷贝)
filesCopy(addFileNames, nowDir, diffDir + "\\"
+ currentTime);
filesCopy(updateFileNames, nowDir, diffDir + "\\"
+ currentTime);
long endTime = System.currentTimeMillis();// 结束时间
logger.info("----运行结束,耗时" + (endTime - startTime) + "ms----");
// 6.写入程序运行时间到日志文件
try {
FileUtils.write(addUpdateLogFile, "----运行结束,耗时"
+ (endTime - startTime) + "ms----" + "\r\n", "UTF-8", true);
FileUtils.write(deleteLogFile, "----运行结束,耗时"
+ (endTime - startTime) + "ms----" + "\r\n", "UTF-8", true);
} catch (IOException e) {
logger.error("将运行耗时写入日志文件出错", e);
}
}
/**
* 将新增的文件和更新的文件复制到第三个文件夹(开源jar包实现文件拷贝)
*
* @param addFileNames
* 增加的文件的名字和相对路径集合
* @param updateFileNames
* 更新的文件的名字和相对路径集合
* @param nowDir
* 源文件所在的目录
* @param diffDir
* 目的文件的目录
*/
private static void filesCopy(List<String> fileNames,
String nowDir, String diffDir) {
File srcFile = null,destFile = null , destFileDir = null;
for (String sourceFileName : fileNames) {
srcFile = new File(nowDir+"\\"+sourceFileName);
destFile = new File(diffDir, sourceFileName);
String fileName = srcFile.getName();
destFileDir = new File((diffDir + "\\" + sourceFileName).replace(
fileName, ""));
destFileDir.mkdirs();
try {
FileUtils.copyFile(srcFile, destFile);
} catch (IOException e) {
logger.error("复制文件出错");
}
}
}
/**
* 获取指定文件夹下面的所有文件,key是文件的名字(去掉基层路径),value是CRC冗余检验码(递归遍历)
*
* @param baseDir
* 基层路径
* @param fileDir
* 真实文件名字(去掉基层路径形成key)
* @param resultMap
* 结果(所有文件的CRC32码,key是真实文件名去掉基层路径,Value是CRC32码)
* @return
*/
private static Map<String, Long> getAllFileNameAndCRC(String baseDir,
String fileDir, Map<String, Long> resultMap) {
File file = new File(fileDir);
if (!file.exists()) {// 文件不存在直接返回
return null;
}
if (file.isDirectory()) {// 如果是目录,继续递归遍历获取其下面的所有文件的CRC32码
for (File f : file.listFiles()) {
getAllFileNameAndCRC(baseDir, f.getAbsolutePath(), resultMap);
}
} else {// 如果是文件,获取文件的CRC32码并添加到map中
long fileCRC = 0l;
try {
fileCRC = FileUtils.checksumCRC32(file);
} catch (IOException e) {
logger.error("获取文件的CRC32出错");
}
resultMap.put(file.getAbsolutePath().replace(baseDir, ""), fileCRC);
}
return resultMap;
}
/**
* 将当前日期转换为指定格式的字符串
*
* @return yyyy年MM月dd日HH时mm分ss秒 格式的日期串
*/
private static String convertCurrentTime2String() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
return sdf.format(new Date());
}
}
总结:
了解到使用CRC32对文件进行校验,如果文件未修改CRC32码是不会变的。获取文件的CRC32码也有好多种方法,利用FileUtils可以直接获取,第二种获取方式如下:
/**
* 获取文件的CRC
*
* @param file
* 需要获取CRC32码的文件
* @return 文件的CRC32循环冗余码
*/
private static long getFileCRC(File file) {
BufferedInputStream bsrc = null;
CRC32 crc = new CRC32();
try {
bsrc = new BufferedInputStream(new FileInputStream(file));
byte[] bytes = new byte[1024];
int i;
while ((i = bsrc.read(bytes)) != -1) {
crc.update(bytes, 0, i);
}
} catch (Exception e) {
logger.error("计算文件的CRC32循环冗余检验出错", e);
} finally {
if (bsrc != null) {
try {
bsrc.close();
} catch (IOException e) {
logger.error("计算文件的CRC32循环冗余检验出错", e);
}
}
}
return crc.getValue();
}
补充:关于基本数据类型的转换问题
byte-1,short-2,int-4,long-8,char-2,boolean-1,faloat-4,double-8 (char型可以转换成int型,我一直以为不能。)
数字类型六种:(四个整数型(默认是int 型),两个浮点型(默认是double 型))
int 型会隐士的转为long,long转为int需要显示的转换,有可能造成溢出;浮点数默认是double类型,如果是float需要声明或强转。
package cn.xm.exam.test;
public class Test1 {
public static void main(String[] args) {
long maxValue = Long.MAX_VALUE;
System.out.println(maxValue);
int ii = (int) maxValue;
System.out.println(ii);
}
}
结果:
9223372036854775807
-1
- 下面是摘自深入理解JVM的解释
类型转换指令可以将两种不同的数值类型进行相互转换,这些转换操作一般用于代码中的显示类型转换,或者用来处理字节码指令集中数据类型相关指令无法与数据类型一一对应的问题。
Java虚拟机直接支持(即转换时无需显示的转换指令)以下数值类型的宽化类型转换(即小范围类型向大范围类型的安全转换):
(1)int型到long、float或者double的转换。
(2)long类型到float、double类型
(3)float类型到double类型
相对的,处理窄化类型转换时,必须显示地使用转换指令来完成,这些转换指令包括:i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l和d2f。窄化类型转换可能会导致转换结果产生不同的正负号、不同的数量级的情况,转换过程很可能会导致精度的丢失。
在将int或long类型窄化转换为整数类型T的时候,转换过程仅仅是简单地丢弃废除最低位N个字节以外的内容,N是T的数据类型长度,这将可能导致转换结果与输入值有不同的正负号。这点很容易理解,因为原来符号位处于数值的最高位,高位被丢弃之后,转换结果的符号就取决于低N个字节的首位了。
在将一个浮点数窄化转为整数类型(int或者long)的时候遵循以下规则:
如果浮点值是NaN,那转换结果就是int或者long型的0
如果浮点值不是无穷大的话,浮点值使用IEEE 754的向零舍入模式取整,获得整数值v,如果v在目标类型T(int或者long)的范围之内,那转换结果就是v。
否则,根据v的符号,转换为T所能表示的最大或者最小整数。
尽管数据类型窄化转换可能会发生上限溢出、下限溢出和精度丢失等情况,但是java虚拟机规范中明确规定数据类型的窄化转换指令永远不可能导致虚拟机抛出运行时异常。
补充:一道牛客上面的笔试题,对数字类型的转换了解透彻
int x = 1;
float y = 2;
System.out.println(x / y);
结果是: 0.5
我们查看编译后的代码如下:
import java.io.PrintStream;
public class Test
{
public static void main(String[] paramArrayOfString)
{
int i = 1;
float f = 2.0F;
System.out.println(i / f);
}
}
解释:
本题的意义在于两点,明白这两点之后题会不会本身就不重要了:
①float x = 1;与float x = 1.0f,这两种对于float类型的变量来说定义的方式都是正确的,也是比较常见的笔试题里面考察类型转换的例子,当第一种情况时,是将低精度int向上转型到float,是由于java的特性导致而不需要进行强制转换,而第二种情况则是比较正式的对于float变量的定义,由于这种类型本身在工作项目中并不常见,常用的带小数的数字我们一般都直接使用double类型,而double类型直接定义是没有问题的:double x = 1.0。而由于float的精度没有double类型高,因此必须对其进行显示的格式书写,如果没有这个f,就默认是double类型了。当然double x = 1.0d也是正确的命名,不信你可以尝试,虽然这是一个令人窒息的操作。
②当多个精度的数字同时进行运算时,最终结果以最高精度为准。在多数情况下,整数和小数的各级混合运算中,一般结果都是double类型的。但就本题而言,结果是float类型的,因为x,y两个数字精度最高的就是float,所以最终结果是0.5,并且这个0.5是float类型的。为什么说不是double类型呢,当然如果你这样处理:double m = x/y,当然m是double类型的,也不会报错,而如果你写成int m = x/y,编译器报错提示的时候就会让你转换成float或者进行强制转换成int,他是不会提示你转换成double的,尽管这么写并没有报错,原因就是①中所说的向上强转。float转换成double不需要任何提示。
总结:
不同基本数据类型转化与运算byte-short-int-long-float-double,float=2转化为float=2.0F,int/float相比先转化为同一类型即float(1.0)/float(2.0)=0.5
来源:oschina
链接:https://my.oschina.net/u/4277230/blog/3934309