第一篇:java 问题汇总(总结,重点)
在用到spring框架中时,场景如下
post 请求过去,对象接收不到参数的值(解决办法:考虑到在参数上加个@RequestBody 注解即可,有些没加的是框架帮忙处理了后默认接收的是json串)http://localhost:8080/xxxxxxxxxxx-xxxxxxx-api/xxxxxx/xxxxx/xxxxxxxxx/suggest/add.sgt
======================================== mapper.xml->@Repository{存储数据层}->@Service{业务层}->@Controller{展示层}(spring注解可以理解为这样的线性,任其项目结构怎么变这样的线性结构是不会变的,万变不离其宗)
@Repository @Service @Controller均是注册在spring上下文中 @Autowired 在spring上下文中找bean
@Qualifier 配合 @Autowired使用 当找到多个同一类型的bean,则会抛异常,此时可以使用 @Qualifier(“beanName”),明确指定bean的名称进行注入 @RequestMapping 配置连接
@Required 注册在sett方法上,检查有没有被调用 @RequestParam 绑定参数
@RequestBody读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上
如果你也用过struts2.简单介绍下springMVC和struts2的区别有哪些? springmvc的入口是一个servlet即前端控制器,而struts2入口是一个filter过虑器。
springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。2.Spring的事务是如何配置的?
先配置事务管理器TransactionManager,不同的框架有不同属性。再配置事务通知和属性,通过tx:advice。配置,设置那些方法或者类需要加入事务。
3.spring事务控制放在service层,在service方法中一个方法调用service中的另一个方法,默认开启几个事务?
spring的事务传播方式默认是PROPAGATION_REQUIRED,判断当前是否已开启一个新事务,有则加入当前事务,否则新开一个事务(如果没有就开启一个新事务),所以答案是开启了一个事务。4.spring 什么情况下进行事务回滚?
Spring、EJB的声明式事务默认情况下都是在抛出unchecked exception后才会触发事务的回滚
unchecked异常,即运行时异常runntimeException 回滚事务;checked异常,即Exception可try{}捕获的不会回滚.当然也可配置spring参数让其回滚.spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commit or rollback(Spring默认取决于是否抛出runtime异常).如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。
一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtime exception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。5.Spring支持的事务管理类型? Spring支持两种类型的事务管理:
编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
6.Spring框架的事务管理有哪些优点?
它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如 它支持声明式事务管理。
它和Spring各种数据访问抽象层很好得集成。7.讲下Spring的七大事务传播?
Spring中通过Propagation来设置事务的传播属性的,在这个属性中提供了我们其中关于事务传播的特性:
PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。PROPAGATION_NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
8.事务并发会引起什么问题,怎么解决?
事务并发会引起脏读,幻读,不可重复读等问题,设定事务的隔离级别就可以解决。
9.事务的安全问题:锁机制的实现原理及在项目中的使用?
锁有悲观锁和乐观锁,悲观锁一般假设每个人都会修改数据,默认情况下把数据都锁住,影响性能,但安全性高.乐观锁是假设每个人都只读下数据,不会修改数据,性能比较高,但是安全性较低,一般通过增加类似于版本控制里面版本号来解决问题。10.讲下BeanFactory和ApplicationContext的区别?
BeanFactory是Spring容器顶级核心接口,比较早,但功能比较少,getBean就是BeanFactory定义的。
ApplicationContext是Spring里面的另外一个容器顶级接口,它继承于BeanFactory,但是提供的功能譬如校验,国际化,监听,对Bean的管理功能比较多,一般使用ApplicationContext。11.简单介绍下你对mybatis的理解? mybatis配置
SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。12.讲下MyBatis和Hibernate的区别? MyBatis是JDBC的轻量级封装,把Sql和java代码独立出来,性能相对比较高,写SQL语句相对于比较灵活,并且容易调试,一般用在大型项目中.Hibernate是JDBC的重量级封装,开发速度比较快,但是性能比较低,调试不方便,一般适合对进度要求的比较高的中小型项目.13.mybatis中#{}和${}的区别是什么?
${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver。#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值,比如ps.setInt(0, parameterValue),#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()。14.mybatis中XML映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签?、、、、,加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。15.MyBatis编程步骤是什么样的? 创建SqlSessionFactory 通过SqlSessionFactory创建SqlSession 通过sqlsession执行数据库操作 调用session.commit()提交事务 调用session.close()关闭会话
16.JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?
① 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。②Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
③向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决: Mybatis自动将java对象映射至sql语句。
④对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。解决:Mybatis自动将sql执行结果映射至java对象。17.使用MyBatis的mapper接口调用时有哪些要求? Mapper接口方法名和mapper.xml中定义的每个sql的id相同
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
Mapper.xml文件中的namespace即是mapper接口的类路径。18.简单的说一下MyBatis的一级缓存和二级缓存?
Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象 Mybatis的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个namespace中查询sql可以从缓存中获取数据。二级缓存是可以跨SqlSession的。
19.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
20.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复? 不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。
原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。21.为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。22.SSM优缺点、使用场景?
1.Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
2.Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。3.Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
4.总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。23.SpringMvc里面拦截器是怎么实现的?
有两种方式,一种是实现接口,另外一种是继承适配器类,然后在SpringMvc的配置文件中配置拦截器即可:
24.Spring中AOP的应用场景、Aop原理、好处?
AOP--Aspect Oriented Programming面向切面编程;用来封装横切关注点,具体可以在下面的场景中使用: Authentication 权限、Caching 缓存、Context passing 内容传递、Error handling 错误处理Lazy loading懒加载、Debugging调试、logging, tracing, profiling and monitoring 记录跟踪优化 校准、Performance optimization 性能优化、Persistence 持久化、Resource pooling 资源池、Synchronization 同步、Transactions 事务
原理:AOP是面向切面编程,是通过动态代理的方式为程序添加统一功能,集中解决一些公共问题。优点:
1.各个步骤之间的良好隔离性耦合性大大降低
2.源代码无关性,再扩展功能的同时不对源码进行修改操作。
第二篇:JAVA培训总结,重点都有1
学习使人进步 第一课
一、java 语言简介:
是由 sun 公司推出的新一代的纯面向对象的网络编程语言,1995年问世的。
二、java语言的特性:
1、简单的: { keep it simple and stupid(保持它的简单性和可操作性)以人类正常的思维模式思考问题。java 是由c++语言 发展而来。
java 提供了非常丰富的内置资源库(API)}
2、安全的:健壮性 {
1、java 中没有指针(指针,是一项能直接指向内存地址的)
2、java 会内存释放--GC机制(垃圾回收器,自动释放)
3、预检索程序错误--在程序执行前,会检查程序是否有 语法 错误,有则不会允许执行。
4、异常处理机制--}
3、可伸缩的: {
1、平台:把软件的语言,翻译成硬件 所能识别的语言,即程序运行的硬件和软件的环境。
2、java SE--java 标准版(实现 桌面应用程序)
3、java EE--j2EE 企业级版本
4、java ME--java 微型版本(嵌入式 开发)}
4、跨平台的: {
}
5、面向对象的: {
1、现实世界是什么样子的:(山川、河流、动物、植物。。分类组成的)
现实世界 就是对所有的事物进行分类,分类就代表一个群体。
分类就是将具有相同特征和行为的事物进行概括。
每个分类下面都有无数的个体,在java 语言中,和现实世界是一样的,也是要先分类再去实现分类下的个体(java 中将 个体称为:对象)
根据:特征(属性)、行为 划分的2、java 语言中,类和对象的关系:
java 语言中将现实世界中表示群体的分类称之为类,而是将群体下面的每一个个体称之为对象;
具有相同属性和行为的 对象的集合 抽象为类或称之为类型
对象是类的一个具体的实例,展示了类的不同属性和行为
实例化:就是将抽象的类 转化成具体的个体的过程
3、如何用java语言去描述类:
java 语言中类的描述分为两部分:
1、要描述 他的属性(特征、静态属性)
2、要描述的是行为(动态属性、方法)语法格式:
class class_name{静态属性;动态属性;}
类
类名
类体
静态属性:相当于 字段 动态属性:用方法描述 }
主控制类:
1、声明对象
并对 对象 进行 实例化对象
类型名 对象名=new 类型名()//声明部分
2、使用对象,即向实体类发送消息。
对象名.属性名=“属性值”-----字符串用“" 数值 不用
对象名.方法名()
第二课
跨平台性:
*
一、java如何实现跨平台的?
1、平台:把软件的语言翻译成硬件所能识别的语言,即程序运行的软件或硬件环境。
java 之所以能够实现跨平台性,是因为 java 有属于自己的平台,不直接与操作系统或硬件环境交流。
java平台系统由两部分组成:
1、java开发工具包;
2、java运行的软环境(java虚拟机--JVM---java virtual machine)
API:是一些已编写好的程序的集合,他提供java 面对程序员的接口。
接口:很多内置资源中,有一个数据包存储对市面上已经存在的各种操作系统进行解析。
java虚拟机:(JVM)就是一个小型的计算机,也可称为 java的解释器。作用:是翻译、解释java 的语言,只认识字节码文件(.class文件)。
java 实现跨平台的原理:
.java 是程序员所能够识别的代码,通过 编译器 转换成 解释器 所能够识别的.class字节码文件,解释器 将.class字节码文件 转换成目标机器 代码文件,以
上就是java 实现跨平台的原理。
*面向对象和 面向过程 两者之间的区别:
1、面向过程:是分析出解决问题所需要的步骤,然后用方法 把这些步骤一步一步的实现,当使用的时候,一个一个 依次调用。缺点,代码的可重用性和可维护性 较差。
2、面向对象:是把构成问题的每一个对象 进行分类,分类的目的不是为了解决某一个步骤,而是为了描述 某个事物在整个解决问题步骤中的行为。优点:代码的可重用性和可维护性较强。
分类--创建对象--发送消息
*作业1:用自己的语言 描述 类、对象、实例化,三者之间的关系。
类 就是拥有相同特征和行为 的 某一群体,而,对象就是 这一群体 中的某一个体,而实例化 是对群体声明,使其从死的变成活的,使其从不占用系统内存--到占用系统内存。类--人 个体--王宁
实例化--王宁定义成人 的过程
作业2:使用 java 语言 描述 汽车类(品牌、颜色、车牌号、排气量)行为:汽车运行。作业3:用java 语言,描述 门 类型(长、宽、材质)行为:求面积、求周长
第二章
方法的设计
第一课
方法的语法规则:
[限定修饰词] 返回值类型 方法名([形参列表])//方法头 {
方法体;//方法所要执行的一系列的语句(可以是一条或多条语句)}
只要是被称之为 语句的都要 放在方法体中 执行。
一、限定修饰词 作用:对类的成员施以一定的 访问权限限制,实现类中的成员一定范围内信息的隐藏。---限制方法被调用的范围。
1、public 公共的,公有的,可以被 所有的类 进行访问。对所有用户开放,所有的用户都可以直接进行调用。
可以被 当前类下的程序访问,同一包(同一文件夹)下的类访问,子孙类访问,其他包下的类访问。
2、protected 受保护的,可以被 同一包下的类 访问,父类 和 子类 的继承关系中 也可以访问。
3、friendly 友好的,默认的限定修饰词,可以被当前类或 同一包下的类访问。
4、private
私有的,只能有本类 来进行访问。
作用域
当前类
同一包
子孙类
其他包 public
*
*
*
* protected *
*
*
x friendly *
*
x
x private
*
x
x
x
二、返回值 类型:
1、有返回值
⒈基本数据类型
整形的 byte(字节数据类型,1个字节)、short(短整型,2个字节)、int(4个字节,默认的整形)、long(长整形,8个字节)
浮点数 float(单精度浮点数,4个字节)
double(双精度浮点数,8个字节,默认的类型)
其他
char(字符数据类型,1个字节,用''号)
boolean(布尔数据类型,占1/8字节,只有
true/false)
⒉复合数据类型(引用数据类型)
类类型
数组
枚举
接口
泛类型
标注
(如果方法设定时,将其设定为有返回值类型的,那么,在方法体内 必须有一条return 语句。return语句的作用:
1、返回给 调用者一个确切的值,而且该值的数据类型,必须与方法定义的返回值类型相一致。return语句 使用的注意事项:
1、一个方法体内只能出现一条可执行的return语句;
2、return语句后 可以是常量、变量或 表达式,但是 必须保证只能得到一个准确值;
3、return语句 有结束方法体的作用,所以在return语句后 不允许出现任何的可执行语句。)
2、无返回值 void
(如果是无返回值类型的,往往方法体内描术的是一系列的动作,不需要给方法调用者 一个返回值。)
有返回值 与 无返回值 在执行上的区别:
1、无返回值方法的调用步骤:传递参数、执行方法体中的各条语句
2、有返回值方法的调用步骤:传递参数、执行方法体中的语句、返回给调用者一个
确切的值
三、方法名
标识符的命名规则和规范:
规则:
1、必须以字母、下划线、或美元符号开头,后面可以跟随字母、下划线、美元符号、或数字。
2、区分大小写。
3、不可以单独使用java中的关键字和保留字。
类名的规范:
1、简单单词做为类名,要求首字母大写,其余字母小写。
2、复合单词作为类名,要求每一个单词的首字母都要大写,其余字母小写。
变量名及方法名的规范:
1、简单单词作为变量名及方法名时,要求所有字符全部小写。
2、复合单词作为变量名及方法名时,要求第一个单词所有字母小写,从第二个单词之后首字母大写,其余字母小写。
其他要注意的规范:
1、严格区分大小写。
2、一个源文件中,只允许写一个类。
3、源文件的名字要与类名字,相一致。
四、参数
参数:在调用方法的过程中,可以由参数对数据进行传递。参数的分类:
1、形式参数:是在方法定义时,所定义的变量,就称之为形式参数,或简称为形
参。可以一次定义多个,中间用,号分隔。
:是方法 接受参数的占位符。
2、实际参数:调用方法时,为形式参数所传递的具体值,就叫做实际参数。简称为 实参,实参可以有多个,中间用,号分隔。
形式参数在使用时的注意事项:
1、形式参数可以有一,个到多个,中间必须用,号分隔。
2、每一个形式参数就是声明一个变量,但是不能为其赋值。
3、在我们的方法体内,可以直接使用声明的形式参数。
4、形式参数 只能在调用方法的时候,由方法的调用者为其赋值。实际参数在使用时的注意事项:
1、实际参数的数据类型 必须与形式参数的数据类型相一致 或相兼容。
2、实际参数的顺序必须 与形式参数定义的顺序相一致。
3、实际参数的个数必须与 形式参数的个数相一致。
参数传递的分类:
1、值传递:就是基本数据类型之间 数据的交互就称之为 值传递
2、地址传递:就是对象 与 对象之间 数据的传输。
有三个类
男 女 中
男要向女
送一朵
玫瑰花
某男孩 喜欢 某女孩
送一朵 玫瑰花
第二课
一、事件委托机制
就是两个类型不想做正面接触,又想达到一定的目的,可以委托第三类完成。
方法体:就是程序功能的描述。通过运算符和表达式体现出来。
运算符 与 表达式:
运算:就是对数据进行的操作。
运算符:就是运算时所使用的符号,称为运算符。操作数:参与运算的数据。
表达式:由操作数和运算符,按一定语法形式 组成的有意义的符号序列。结合性:左,从左到右 开始运算
右,从右向左 开始运算
运算符:
1、赋值运算符:
变量名 = 值;(= 是将右边的值 赋给 左边的变量)
复合赋值运算符: +=、-=、*=、/=、%=
2、算术运算符:
1、一元
---元:指操作数的个数(+正号,-负号,++自增1 符号,--自减1 符号)例:int a=3
int b=++a 前缀
int b=a++ 后缀
如果 自增或自减 运算符,在变量之前,那么 我们就先自加1 或 自减1 后在使用,如果自增 或 自减 运算符 在变量之后,那么 我们就先使用,后自加1 或 自减1 的操作。
2、二元(+、-、*、/、%)
如果两个操作数 都为整数的情况下,/ 表示整除,那么得到的结果就是 整数。
+、-、*、/ 中 只要操作数是整数,的到的结果 就是整数,操作数 是浮点数,得到的结果就是浮点数。
% 取模 操作数1 和 操作数2 如果 是负数的话,看% 符号,左边的 操作数符号 赋给最终结果。
如果操作数有一个负数,先忽略操作数的符号,进行运算,然后,将左边操作数的符号 赋给 最终结果。
浮点数取模:a%b=a-((int a/b)*b)总结:
3、三元(表达式1? 表达式2:表达式3;)
判断表达式1 的结果(必须是 布尔类型)如果是 true 值 时,执行--表达式2,如果是false 值,执行--表达式3;
先算一元,再算二元,最后算三元。
3、其他运算符
编写程序的三点原则:
1、就是保证程序运行速度快,每一条语句 尽量做到 优化。
2、就是节省内存资源。
3、保证程序运行的安全系数。
*** 如果一个类
是 public 来修饰的,类名 必须 与原文件名
相同。
第三课
一、关系运算符(>、<、>=、<=、==、!=)
任何一个 由关系运算符 链接的表达式,其返回值 一定是一个 布尔类型的值。
二、逻辑运算符(&& 与、|| 或、!非)
逻辑运算符 链接的表达式(前面或后面)
必须是一个布尔 数据类型的值,或能够 获得布尔值的表达式。
对“||”运算,如果左边的表达式的值为true,则不必对运算符右边的表达式再进行运算,整个表达式的结果就为true。
对“&&”运算,如果左边的表达式的值为false,则不必对运算符右边的表达式再进行运算,整个表达式的结果就为false。
(先算 非!,再算 与&&,最后算 或||)
位运算符:对二进制 数进行的运算。
~ 按位取反:对数据的每一个二进制位 取反运算,1--0,0--1
& 按位 与运算 : 要求有两个操作数进行运算,如果两个相应位均为一,则该位结果为1,否则结果为0。8&9--1000&1001
| 按位或 运算:
8|9 要求有两个操作数进行 运算,只要 两个相应位 中 有1 个 为1,则该位 结果为 1,否则 该位结果 为 0。
^ 按位 异或 运算:相同数值 为假,不同数值 为真。
相同的数字 进行按位异或运算 得到的结果 及 为0,不同的数字 进行按位异或运算 得到的结果 及为 1。
<< 左移 运算:x<<,箭头 指向 则是要 移的 方向。
x 左移 移动n 位,就相当于 x*2的n次幂。
>> 右移 运算(算术右移--相当于除法运算):8>>2 === 8/2
x右移 n位,就相当于 x/2的 n次幂。
>>> 逻辑右移 : 不管正数 还是 负数,空出来的左侧 的高位,全部补 0。
逻辑右移 又被称为 不带符号位的右移,x逻辑右移n 位,就是将x 的各位 右移n 位,移到右端的低位 舍弃,左移后 左边 留下的空位 一律 补0.负数的二进制数 求法: 先忽略 符号,得到正数 的二进制数,在进行 按位取反,在在最后一位加一。
空出来的高位,正数 用 0 补,负数 用 1补。
逻辑与和或 与 按位与和或 的 区别:
1、&& 逻辑与 ||或运算符,& 按位与 |或 运算符。
2、逻辑与 ||或 的两个操作数 都必须 是布尔数据类型,而 按位与 |或 的操作数 可以是任意的 基本数据类型。
3、逻辑与 ||或 有短路功能,而 按位与 无短路功能。
1、new 运算符
:就是对 对象 进行实例化。
2、对象名 instance Of 类名
判断你所给出来 的 对象 是否 属于这个类 得到的结果 是布尔类型值,返回值 为true 说明 该对象 属于该类,如果 返回false 说明该对象 不属于 该类。
第四课
一、复合语句:
1、流程控制语句:
循环语句---使用的时机,当程序中 有一条 到多条 需要 重复执行时,我们就要用到 循环语句。
1、当型 while 循环
while(条件表达式)--当 条件表达式 满足的时候 进入 循环体
{
循环体;
}
工作流程:while 是当的意思,当条件表达式的 内容为真时,进入循环体,循环体 执行结束后,再一次判断循环条件,如为 真,继续回到 循环体 执行,直到循环条件为假 时,终止 循环。
在使用 while循环时要考虑的内容:
1、什么时候进入循环体,什么时候退出循环体;
2、进入循环体之后做什么;
2、直到型循环(do……while 循环)
do
{
循环体;
}while();
do。。while 循环的工作流程:在没有 判断循环条件的情况下 进入循环体,循环体 执行结束后,判断 是否满足 循环条件,如果 返回值 为 true,在一次 进入到 循环体,直到 返回 false 为止。
while 与 do。while 的区别:
1、while 是先 判断条件 在 决定 是否 进入循环体。
do。while是先进入 循环体,然后在判断条件,在决定 是否继续进入循环。
2、使用 do while 循环时,循环体 至少被 执行 一次。
3、for 循环
for(表达式1;表达式2;表达式3)
{
循环体;
} 表达式1:循环变量的初始值。
表达式2:循环条件,得到的 必须是一个 布尔 值。表达式3:循环步长,循环变量的增量值。
要注意的:
1、可同时为空,当所有条件 都为空时,表示的是 死循环,2、表达式1,与表达式3 的位置 可以同时存放多个 表达式,中间用逗号,分隔。
3、表达式2 的位置,必须返回 一个布尔值。
4、循环体 可以为空。
工作流程:
1、执行表达式1;
2、执行表达式2,判断条件是否成立;
3、执行循环体;
4、执行 表达式3;
5、回到 表达式2,判断是否成立,成立3--4--5,不成立 退出循环;
第五课
Integer.parseInt(args[0])
// 转换 成 int 类型
System.err.println(”请输入一个值");//从标准 错误流
中 输出 一句话。System.exit(-1);// 退出 java 虚拟机,exit 方法 需要 传递 一个int 类型的参数,此参数只能是 0 或-1。当传递值为0 时,表示正常状态退出 java虚拟机;当参数为-1时,表示非正常状态 退出java虚拟机。
}
1、命令行参数传递给谁?
传递给main方法中定义的String类型的args数组。
2、命令行出现多个参数由谁分隔?
多个参数通过空格分隔
3、命令行传递的值是什么数据类型?
String数据类型
4、令行参数如何进行数据类型的转换?
1)转换为int类型
Integer.parseInt();
2)转换为double类型: Double.valueOf(args[0]).doubleValue();
5、String args[]中args是否可由其他名字来代替?
可以
6、String[]args和String args[]两种写法是否都是正确的?
两种写法都是正确的。
方法
分三类:
1、构造方法:又叫做 构造函数、构造器,构造方法名 与类名 相同。没有 返回值,但是 不用 void 修饰。
一个类中,可以有一个到多个 构造方法。如果 有多个构造方法,那么 要求 每个构造方法的参数类型、个数 一定不同。由此,决定调用哪一个构造方法。
构造方法 只能通过 new 运算符调用。
作用:
1、在内存中分配一块空间,给 实例化的对象;
2、为成员变量 初始化;
默认的构造方法:参数体、方法体 为空,在一个类中,只要人为的 设置了 构造方法,那么,默认的构造方法就会被 销毁。
2、实例方法:必须由对象 调用的方法。
3、静态方法:又叫做 类方法,是由static 来修饰的方法。可以由 对象名 或 类名直接调用。
main 方法 由java 虚拟机 自动调用 而不是由 用户显示调用。
作业:
1、为什么 main 方法 必须由 static 来修饰? main 作用:1声明对象
2、传递信息
在main方法加载之前是没有对象的,但 main方法 可以加载,就说明是由类名调用,main方法 是静态方法,所以 必须 由 static 来修饰。
2、设计一个 三角形的类,构造方法
为三角形的 三边 初始化;返回 三角形的周长;面积,高 通过形参 获得;在main 方法 中 判断,同过 命令 行 传递的 三个值 是否能够成 三角形。
第六课
嵌套:包含的意思。在一个完整的结构内,包含 另一个结构。
循环嵌套:在一个完整的循环内 包含另一个 循环。
外层循环;
// 在循环体内,包含 另一个 完整的循环 语句,就叫做 外层循环。{ 内层循环;
// 放在 另一个 循环语句体内 的 循环,就叫做 内层循环。
}
循环 可以嵌套 若干层。
for(表达式1;表达式2;表达式3){
for(表达式4;表达式5;表达式6)
{
内层循环体;
} }
工作 流程:
1、表达式1;
2、表达式2;
3、表达式4;
4、表达式5;
5、内层循环体;
6、表达式6;
7、表达式5; 为真:4、5、6 直到表达式5不成立;
8、表达式3;
9、表达式2;
为真:2、3、4、5、6、7
口诀:外层循环 执行一步,内层循环 要执行 满足条件的 一轮。
第七课
流程 控制语句:
二、条件分支语句:
1、if 不带else :if(条件表达式)
{
语句体;
}
带 else : if(条件表达式)
{
语句体1;
}
else
{
语句体2;
}
if的嵌套使用: if(条件表达式1)
{
语句体1;
}
else if(条件表达式2)
{
语句体2;
}
......else
{
语句体n;
}
2、switch case
switch(表达式)// 他的 返回值 必须是 byte、short、int、char、enum(枚举)
不可以 出现其他数据类型的返回值,而且 返回的 必须是 一
个常量值 { case 常量1: 语句体1;[break;] case 常量2: 语句体2;[break;].......default:语句体n;[break;] }
常量 可以有多个,但不可以出现 相同的常量值。
为什么 switch比 if 嵌套 执行效率高?
:所有流程控制语句,都是由 cpu 内部提供的运算器中的高速缓存来处理,高速缓存 处理if 语句时,先找到条件1,如果 判断为turn,在找条件2,判断为turn 在找条件3,以此类推,直到所有 条件都判断完毕,才会 确定要执行的 语句体。而,在执行switch 语句时,先去找 满足条件的 找到,就执行相应 的语句,没找到 就退出。由此看来,switch 语句 比 if 嵌套语句的执行 效率高。
第八课
跳转语句(转向语句):
一、cantinue:作用:退出本次 循环。
1、不带标号的:语法:
循环
{
[条件判断]
{continue;}
} 执行过程:进入循环体内,判断 条件是否成立,成立执行 continue;语句,从而 跳过continue;语句 后面的其他语句,完成 本次循环。继续 进行下一次 循环的 条件判断,已决定 是否 继续 循环。
t:相当于 table 位,及 4个 空格。
2、带有标号的:(goto)标号:就是 给循环 起个名字。
结束 标号 所指名的循环
本次循环,继续
标号 所指名循环的 下一次循环条件 判断,继而 决定 是否 执行 下一次循环。
语法:
标号:
循环
{
循环
{
[条件判断]
{continue 标号;}
}
}
使用标号要注意内容:
1、标号的后面 应该 紧随 一个循环 结构,否则 编译出错;
2、标号单独使用 没有任何意义,要与 break 或 continue 一起使用;
3、标号 对于单层循环来说 意义不大。
二、break:终止循环的作用。
1、不带标号的:作用:结束 break 所在循环,转去执行 该循环体 后的其他语句。
语法:
循环
{
[循环条件;]
{break;}
}
其他语句;
2、带有标号的:作用:终止 标号 所标示 的循环体,转去 执行 该循环 外的 其他语句。
语法:
标号:
循环
{
循环
{
[循环条件;]
{break 标号;}
}
}
continue 与 break 的 区别:
1、continue 是用来 跳出 某一次 循环。而
break 是终止循环。
2、continue 只能出现在循环 结构中,而 break 除了可以出现 在循环结构 中之外,还可以出现 switch 开关 语句中。
作业:
1、输出1-1000 能 被 3整除的 前10个 较小的数,每行 输出 5个值。
2、找出1-100 之间,第 3个 既能被 4整除,又能 被7整除的 数。
第九课
算法:
1、算术模型算法
2、非算术模型算法:解决三方面:
1、代码的重用性;
2、代码的可扩展性;
3、代码
的可维护性。
1、穷举(枚举)算法:定义:遍历一个域,穷尽 该域 范围内的所有可能。
性能最差的一个算法。
使用时机:只有一个 查询条件,可是要查询的数据 是海量的,此时就要用
到 穷举算法。
使用穷举算法需要确定的三个因素:
1、需要确定 循环的范围,将可能的范围一一列举出来,不重复,不遗漏;
2、确定筛选条件,根据条件对结果,逐一判断,挑选出符合条件的;
3、优化,壳体条件是在保证,不遗漏掉 解 的情况下,尽量减少 循环的层数和 循环的范围。
2、迭代算法:由一个已知的值,不断地去推出新值的过程。
使用时的要素:
1、确定迭代变量的初始值;
2、迭代公式:就是能够从变量的当前值,推出下个值得公式;
3、迭代的范围,迭代的终止条件。
3、递归算法:就是方法的自调用。(两部分:
1、递推;
2、回归)
1、迭代的公式;
2、终止的条件;
3、初始值的确定(确定每一次调用方法时的参数,及返回值)
重复:
1、每一次方法的调用,都是在规模上有所缩小;
2、相邻两次的调用有紧密的联系,前一次要为后一次做准备;
3、在问题规模极小 的情况下,必须用直接给出解答的方式,而不能在递推调用,因此,每一次递归 调用 都是有条件的。无条件的递归 调用,终将成为死循环。
在一个类中,相互调用 静态方法时,类名 可以不写。
第二章总结:
在设计方法时 应秉承的原则:
1、在一个方法体里,尽量少的书写 代码;一个方法体中 只完成一个功能。
2、要有好的 运行效率:
1、尽可能少的占用资源(内存资源、和 cpu(中央处理器)资源)
第三章
数据的设计
第一课
一、变量的声明语法:数据类型
变量名
声明变量中 数据类型的作用:
1、开辟多大的空间;
2、存放 哪种类型的数据
二、数据类型
1、基本数据类型-----所声明的叫
变量
{ byte、short、int、long
float、double
char、boolean
int a=3 ;----叫做 值引用
基本数据类型 在java 虚拟机 的运行原理:
java 虚拟机 看到 int 类型的 a 变量,就会 开辟一个 4 个字节的 空间,名字 叫做 a,每个 基本数据类型的变量 只能对应 一个 准确的 值,所以我们 将其 称之为 值引用。
基本数据类型的两大特点:
1、值引用
2、基本数据类型 可以参与 各大数据类型的运算。
2、复合数据类型------所声明的叫
对象
{ 类类型、数组、标注、泛类型、枚举、接口
两大特点:
1、可以实现 地址 引用;
2、复合数据类型 不可以 参与各大数据类型的运算,但是 可以 比较地址是否相同(==)。
三、变量的 初始化
分为:
1、直接初始化 ;//a=3
2、间接初始化 ;//b=a*2
一个基本数据类型的变量 在使用之前,一定要对其 进行 初始化,否则 该变量无法使用;成员变量的初始化 由构造方法 实现;静态方法的初始化 由 静态初始化器实现;局部变量在使用之前 必须 赋值。
1、自动类型的转换也成为 隐式转换;
小空间 向 大空间 转换时,可以自动(直接)进行转换的叫做:自动数据类型的转换 特例:
1、long 数据类型 可以自动 转换成 float、或 double 数据类型
2、byte 数据类型 不能 自动转换成 char 数据类型
3、布尔数据类型 不参与自动数据类型的转换。byte---short---int---long---float----double
char---
自动类型 转换的时机:
1、赋值时;
2、方法调用时;
3、算术运算时(一元运算中:
1、操作数的数据类型是byte、short、char 时,结果将自动转换成int数据类型;
2、++ 或--运算 等到的结果,任然是操作数本身的数据类型,及++ 或--符号不参与 自动数据类型的转换
二元运算中:
1、在大多数情况下,两个操作数进行运算,会自动转换成范围 较大的数据类型;
2、假设 操作数类型为 byte、short、char 的情况下,运算结果会自动转换成 int数据类型;
3、复合赋值运算符,不参与 自动数据类型的转换)
2、强制类型的转换也叫做 显示转换;
大空间 向 小空间 转换,用到 强制数据类型的转换;
语法格式:(目标数据类型)常量、变量、表达式;
情况:
1、如果 被转换的数据类型的值的大小 没有超出 目标数据类型的取值范围,则值不发生任何改变;
2、如果要转换的值,超出了目标数据类型的取值范围,那么,用该值 对目标数据类型的值域变化范围
进行 取模 的操作。(该值 大于 目标数据类型 的最大取值,并且超出了其值域变化范围);
3、如果该值 大于 目标数据类型 的最大取值,但是没有超出了目标数据类型值域变化范围,则用 该值-值域变化范围。
4、浮点数 转换成 整数时,直接 舍去 小数部分,保留 整数部分。
5、布尔数据类型 不参与 强制数据类型 转换。
第二课
自动绑定的三部曲:
1、加载 静态初始化器
2、加载主方法
3、通过 new 关键字 调用构造方法
局部变量:
如果 局部变量 与成员变量 同名的情况下,那么在局部变量的作用范围内 隐藏 成员变量,想要在 局部变量 作用范围内 引用 成员变量,那么 要用到
this 关键字 引用成员变量。
相同作用域内 不允许 出现同名 变量,而不相同 的作用域范围内,可以出现 同名变量。
成员变量 与 局部变量的 区别:
1、声明的位置不同,成员变量 在一个类的方法体外 声明;而 局部变量 可以放在方法体内、也可以作为 方法的参数、或 放在 语句块内。
2、作用域不同,一对花括号 就是一个作用域。
3、成员变量的初始化,可以由 静态初始化器、或 构造方法来完成;而 局部变量的初始化 只能由 直接 或者 间接 来完成,而不能由 静态初始化器 或 构造方法来完成。
最终变量:
它是指 在程序 运行过程中,其值 不变的 变量。最终变量在使用前
必须 为其 初始化。
语法:
1、final 数据类型 变量名=赋值;// 声明最终 变量时 直接 初始化。
2、final 数据类型 变量名; // 先声明,在使用之前 在为其 初始化。
变量名=赋值;
目的:防止 值发生 改变。
数组:就是具有 相同类型 名称的 数据的集合。
1、声明的语法格式:
数据类型 [] 数组变量名;
数据类型 数组变量名 [];
2、实例化
声明的同时 对其实例化;// 数据类型[] 数组变量名=new 数据类型[数组长度];
先声明,在实例化; // 数组变量名=new 数据类型[数组长度];
数组长度 只能是:byte、short、int 三种 数据类型,不能是其他数据类型。new 数据类型这样的语句有两个作用:
1、在内存中开辟一块 连续的存储空间,每块空间的大小 由 数据类型来 进行决定;
2、为每一个空间 进行 初始化。
作业:
1、为什么使用数组? :
2、声明,实例化数组的语法?
3、什么是数组元素?
4、如何得到 数组最大下标值?
5、如何为 数组元素赋值?
6、如何遍历数组?
7、数组与 数组 变量区别?
第三课
数组对象的方法:
一、冒泡法排序:
两个循环,外层控制 轮数,内层 控制 每轮比较的 次数。
import java.util.Arrays;// 引包(冒泡法)Arrays.sort(a);
二、在一个数组 当中 查找 元素 所在的位置; int pos=Arrays.binarySearch(sum,15*15);//
采用2分 查找法,先找中间值,小于中间值得向前找,大于中间值得先后找,只能 对有序的数组 进行查找。
三、数组复制
System.arraycopy(Object src,int src_pos,Object dst,int dsc_pos,int length)
Object src: 代表 元素组;
int src_pos:
代表
元素组的起始位置; Object dst: 目标数组;
int dst_pos:
目标数组的起始位置; int length: 要拷贝 数组元素的个数;
数组的特点:
1、数组的长度是固定的,必须在创建数组的时候指定数组长度,数组的长度一经确定,就不能修改;
2、一个数组 只能用来保存 同一数据类型的数据;
第四课
多维数组:(二维数组)
声明:数据类型[][] 数组变量名=new 数组类型[高维长度--必选][低维长度--可选]
高维--一维:行数
低维--二维:每行的个数
例:int[][] i1=new int[2][2];
静态初始化:
int[][] i1={{0,1},{1,2,3},{2}};
第五课 向量:可以看做是 一个大小 可自由伸缩的数组。
三个构造方法: public Vector()
---创建初始大小 为10,容量增加值为0(0代表 无限增加)的空Vector 对象。
public Vector(int initialCapacity)---创建初始容量 为initialCapacity,容量增加值为0 的空Vector 对象。
public Vector(int initialCapacity,int capacityIncrement)---创建初始容量为 initialCapacity,当容量不足时 增加值为 capacityIncrement 的空 Vector 对象。
方法说明
public boolean add(Object o)将指定对象追加到当前Vector对象的末尾。
public void add(int index, Object element)将指定对象element插入到当Vector对象的指定位置处。
public int capacity()返回Vector对象的当前容量。
public void clear()将所有的元素从当前Vector对象中移去。
public boolean contains(Object elem)测试当前Vector对象中是否包含指定的对象。
public void copyInto(Object[ ] anArray)将当前Vector对象中的每一个元素复制到指定的数组中去。
public Object firstElement()返回当前Vector对象中的第一个元素(其下标值为0)。public Object lastElement()返回当前Vector对象中的最后一个元素。public Object get(int index)返回当前Vector对象中指定位置上的元素。
public int indexOf(Object elem)返回当前Vector对象中指定对象elem首次出现的位置,找不到则返回-1。
public boolean isEmpty()测试当前Vector对象是否为空。
public Object remove(int index)将指定位置的元素从当前Vector对象中移去。public boolean remove(Object o)删除当前Vector对象中首次出现的指定对象,若当前Vector对象包含该指定对象则返 回true。
protected void removeRange(int fromIndex, int toIndex)删除当前Vector对象中从fromIndex位置开始,到toIndex-1位置的所有元素。
public void removeAllElements()删除Vector对象中的所有元素,并将其大小重置为0。public Object set(int index, Object element)将Vector对象中指定位置上的元素置换为指定对象element。
public int size()返回Vector对象中元素的数目。
public void trimToSize()将当前Vector对象的容量调整为与其元素个数相同。相信能就一定能
第三篇:JAVA实习生问题
JAVA实习生问题:
1.我班只招三人。2.实习从现在开始,至于结束,暂时没有时间限制,因为公司项目较多,缺乏人手,项目周期长。听他们说他们现在接了一个安徽省的云平台项目,这个就够他们忙的了。如果最后你们能够成长起来,并且愿意留下来,他们也很高兴,也就相当于培养自己的员工。
3.工作内容:项目组带着做项目。相当于一边学习一边动手做。所以兴趣很重要,如果很有兴趣,具有一定的分析问题解决问题的能力,遇到问题,则是自己积极想办法解决,而且觉得很有成就感,在有兴趣的前提下,跟着项目组做项目也是学的最快的;但是如果兴趣并不是很大,那么就变成了压力、痛苦和无聊。
4.此次公司招实习生,对基础没什么要求(只要学过java就行),重要的是兴趣。如果动手能力、自学能力较强,具有较强的分析问题、解决问题的能力,即使基础不是很好,也应该问题不大。有兴趣,遇到问题就不会觉得难,而是自己积极的想办法去解决。
5.对于女生来说,需要考虑的更多一些,比如安全问题,毕竟是在校外实习。这些问题你们自己要考虑到,也都是要让你们自己负责的。要慎重,自己考虑清楚。
6.公司招实习生的目的是帮着项目组做项目。不是很想干的,最好别去。
7.工作有两个方向,软件开发or系统集成,方向自己选,看你们对哪一块感兴趣。如果基础好一点,项目组就带着你们做;如果基础薄弱,不光带着做,还会有培训。实际上带着做项目,是学的最快的,但是前提是有兴趣。至于具体做什么,要看是什么项目,是项目中的哪一部分。
第四篇:Java面试问题
1、引用CSS样式有哪两种方式?
Link,@import2、js怎么控制两个文本框,当选中的时候边框变空,没选中的变会原来的颜色。onFocus=“document.getElementById('text1').style.backgroundColor='#eee'”
onblur=“document.getElementById('text1').style.backgroundColor='#fff'”
3、查询数据库表显示在页面需要新建多少个class文件和xml文件?
4、spring有多少种注入方式?
注入方法1:构造方法方式 2:属性注入 3:静态工厂方法参数注入 4: 接口注入
第五篇:java总结
调用父类构造方法
在子类的构造方法中可使用super(argument_list)语句调用父类的构造方法
如果子类的构造方法中没有显示地调用父类构造方法,也没有使用this关键字调用重载的其它构造方法,则系统默认调用父类无参数的构造方法
如果子类构造方法中既未显式调用父类构造方法,而父类中又没有无参的构造方法,则编译出错
1public class Person {
3private String name;
4private int age;private Date birthDate;
7public Person(String name, int age, Date d){ 8this.name = name;
9this.age = age;
10this.birthDate = d;
11}
12public Person(String name, int age){ 13this(name, age, null);
14}
15public Person(String name, Date d){ 16this(name, 30, d);
17}
18public Person(String name){
19this(name, 30);}
21// ……
22}
1public class Student extends Person {
2private String school;
4public Student(String name, int age, String s){ 5super(name, age);
6school = s;
7}
8public Student(String name, String s){
9super(name);
10school = s;
11}
12public Student(String s){ // 编译出错: no super()13school = s;
14}
15}
对象构造和初始化细节
分配存储空间并进行默认的初始化
按下述步骤初始化实例变量
1.绑定构造方法参数
2.如有this()调用,则调用相应的重载构造方法,然后跳转到步骤5
3.显式或隐式追溯调用父类的构造方法(Object类除外)
4.进行实例变量的显式初始化操作
5.执行当前构造方法的方法体
==操作符与equals方法
==操作符与equals方法的区别:
引用类型比较引用;基本类型比较值;
equals()方法只能比较引用类型,“==”可以比较引用类型及基本类型;
特例:当用equals()方法进行比较时,对类File、String、Date及封装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个实例;
用“==”进行比较时,符号两边的数据类型必须一致(可自动转换的基本数据类型除外),否则编译出错;
由装箱引发的——Integer比较的来龙去脉
前置知识: 众所周之,java是保留了int,char等基本数据类型的,也就是说int类型的并不是对象,然而有些方法却需要object 类型的变量,所以java使用了装箱机制,我们可一自豪的这样声明一个整型变量:Integer a = new Integer(10);那么整型的a也就是对象了,那这句是什么意思呢:Integer a= 10;java中可以这样声明一个对象吗?当然不是,从jdk1.5后,java实现了自动装箱,也就是自动将Integer a =10 中的int类型的10转化为了 Integer类型。好,有了前面的只是我们且先看一个题目:
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a==b);
System.out.println(c==d);
答案是什么呢? 如果您回答true,false,那么很遗憾的告诉你,哈哈,其实你答对了!!
那我们晕了就相差1的两个数为啥走向了“反目成仇”的地步呢?凭啥127等于127,我128就不等于128呢?且听我慢慢道来,Integer a =127,Integer a=128。
127,128应该不会造成什么差异吧,难道是自动装箱的过程有猫腻?找下源码看看:
private static class IntegerCache {
private IntegerCache(){}
static final Integer cache[] = new Integer[-(-128)+ 127 + 1];static {
for(int i = 0;i < cache.length;i++)
cache[i] = new Integer(i128);
}
这是用一个for循环对数组cache赋值,cache[255] = new Integer(255-128),也就是newl一个Integer(127),并把引用赋值给cache[255],好了,然后是Integer b= 127,流程基本一样,最后又到了cache[255] = new Integer(255-128),这一句,那我们迷糊了,这不是又new了一个对象127吗,然后把引用赋值给cache[255],我们比较这两个引用(前面声明a的时候也有一个),由于是不同的地址,所以肯定不会相等,应该返回false啊!呵呵,这么想你就错了,请注意看for语句给cache[i]初始化的时候外面还一个{}呢,{}前面一个大大的static关键字大咧咧的杵在哪呢,对静态的,那么我们就可以回想下static有什么特性了,只能初始化一次,在对象间共享,也就是不同的对象共享同一个static数据,那么当我们Integer b = 127的时候,并没有new出一个新对象
来,而是共享了a这个对象的引用,记住,他们共享了同一个引用!!,那么我们进行比较a==b时,由于是同一个对象的引用(她们在堆中的地址相同),那当然返回true了!!
然后我们在看Integer c = 128;Integer d = 128;这两句。现在不用我说就应该能明白了吧,当数据不再-128到127之间时,是不执行return
IntegerCache.cache[i + offset];这句的,也就是不会返回一个static的引用,而是执行了return new Integer(i);于是当 Integer d = 128 时,又会重新返回一个引用,两个不同的引用
在做c==d 的比较时当然返回false了!
下面附上本程序的字节码以供喜欢底层的读者参考:
Compiled from “CompareInteger.java”
public class CompareInteger extends java.lang.Object{
public CompareInteger();
Code:
0:aload_0
1:invokespecial#1;//Method java/lang/Object.“
public static void main(java.lang.String[]);
Code:
0:bipush 127
2:invokestatic#2;//Method
java/lang/Integer.valueOf:(I)Ljava/lang/Int
eger;
5:astore_1
6:bipush 127
8:invokestatic#2;//Method
java/lang/Integer.valueOf:(I)Ljava/lang/Int
eger;
11: astore_2
12: sipush 128
15: invokestatic#2;//Method
java/lang/Integer.valueOf:(I)Ljava/lang/Int
eger;
18: astore_3
19: sipush 128
22: invokestatic#2;//Method
java/lang/Integer.valueOf:(I)Ljava/lang/Int
eger;
25: astore 4
27: getstatic#3;//Field
java/lang/System.out:Ljava/io/PrintStream;
30: aload_1
31: aload_2
32: if_acmpne39
35: iconst_1
36: goto40
39: iconst_0
40: invokevirtual#4;//Method java/io/PrintStream.println:(Z)V43: getstatic#3;//Field
java/lang/System.out:Ljava/io/PrintStream;
46: aload_3
47: aload4
49: if_acmpne56
52: iconst_1
53: goto57
56: iconst_0
57: invokevirtual#4;//Method java/io/PrintStream.println:(Z)V60: return
}
评论:呵呵,这么想你就错了,请注意看for语句给cache[i]初始化的时候外面还一个{}呢,{}前面一个大大的static关键字大咧咧的杵在哪呢,对静态的,那么我们就可以回想下static有什么特性了,只能初始化一次,在对象间共享,也就是不同的对象共享同一个static数据,那么当我们Integer b = 127的时候,并没有new出一个新对象来,而是共享了a这个对象的引用,记住,他们共享了同一个引用!!
呵呵,博主我被你这句话小小的误导了一下,其实你这里说的原理没错,但是把位置说错了,这段代码只是初始化cache:
static {
for(int i = 0;i < cache.length;i++)
cache[i] = new Integer(i-128);
}
但真正让cache[i]为static变量的是这句代码:
static final Integer cache[] = new Integer[-(-128)+ 127 + 1];