【Java】Java题库大全(史上最全)
- 一、JavaSE基础
- 1、接口和抽象类的异同点
- 2、重载(overload)和重写(overwrite)区别
- 3、StringBuffer、StringBuilder、String三种类的区别
- 4、冒泡排序(Bubble Sort)
- 5、选择排序(Selection sort)
- 6、单例设计模式
- 7、常见的异常类型
- 8、Throws和Throw的区别
- 9、Final,Finally,finalize关键字的作用
- 10、Final关键字的作用
- 11、Hashtable与HashMap的区别
- 12、线程和进程的区别
- 13、实现多线程程序的2种方式
- 14、List,Set,Collection,Collections
- 15、sleep()和wait()有什么区别
- 16、error和exception有什么区别?
- 17、heap(堆)和stack(栈)有什么区别
- 18、GC是什么?为什么要有GC?
- 19、内存泄漏和内存溢出
- 20、运行时异常和checked异常的区别?
- 21、四个访问修饰符合访问级别?
- 22、逻辑运算符:&和&&的区别?
- 23、Java中如何实现序列化,有什么意义
- 24、阐述JDBC操作数据库的步骤
- 25、Statement和PreparedStatement有什么区别?哪个性能更好?
- 26、二分查找法
- 27、提示输入一个数,求阶乘
- 28、斐波纳契数列
- 29、Java中基本数据类型
- 30、遍历D盘下面所有的文件
- 31、ArrayList和LinkedList有什么区别
- 32、面向对象的特征有哪些方面
- 33、String是最基本的数据类型吗?
- 34、有一个字符串,其中包含中文字符、英文字符和数字字符,请统计和打印出各个字符的个数
- 35、写一个程序将D盘下面的一张图片拷贝到E盘
- 36、静态类型有什么特点?
- 37、说一下多态的表现形式?
- 38、线程通常有五种状态
- 39、事务的四大特性
- 40、Java中IO体系
- 41、字节流和字符流
- 42、接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实体类(concreteclass)?
- 43、Class.forName的作用?为什么要用?
- 44、异常的体系结构
- 45、构造函数的特点和作用
- 46、Java标识符的命名规则
- 47、Java关键字
- 48、构造函数的特点和作用
- 49、synchronized关键字的用法?
- 50、启动一个线程是调用run()还是start()方法?
- 51、Swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
- 52、数组有没有length()方法?String有没有length()方法?
- 53、构造器(constructor)是否可被重写(override)?
- 54、阐述静态变量和实例变量的区别
- 55、比较一下Java和JavaSciprt
- 56、JDK、JRE、JVM的区别
- 57、XML和Json的特点
- 58、JDK常用的包
- 59、什么是值传递和引用传递?
- 60、解释一下数据库连接池
- 61、空字符串(“”)和null的区别
- 62、列举java中string类常用方法
- 63、得到Class的三种方式是什么—反射
- 64、什么是迭代器(Iterator)?
- 65、char型变量中能不能存贮一个中文汉字?为什么?
- 66、Java中compareTo和compare的区别?
- 67、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?它们有何区别?
- 68、两个对像值相同(x.equals(y)==true),但却可有不同的hashcode,这句话对不对?
- 69、Java的Socket通信(多线程)
- 70、接口中定义
- 71、Break和Continue
- 72、什么是内部类?StaticNestedClass和InnerClass的不同。
- 73、下面这条语句一共创建了多少个对象:String S = “a”+“b”+”c”+”d”;
- 74、什么情况下finally中的代码不会执行?
- 75、一个“.java”源文件中是否可以包括多个类(不是内部类)?有什么限制?
- 76、Java有没有goto?
- 77、问题:如何将String类型转化成Number类型?
- 78、什么是隐式的类型转化?
- 79、显式的类型转化是什么?
- 80、类型向下转换是什么?
- 81、如何原地交换两个变量的值?
- 82、简述synchronized和java.util.concurrent.locks.Lock的异同?
- 83、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
- 84、线程的sleep()方法和yield()方法有什么区别?
- 85、Math.round(11.5)等于多少?Math.round(-11.5)等于多少?
- 86、编程题:用最有效率的方法算出2乘以8等於几?
- 87、当一个对象被当作参数传递到方法,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
- 88、定义类A和类B如下
- 89、StaticNestedClass和InnerClass的不同?
- 90、abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
- 91、静态变量和实例变量的区别?
- 92、垃圾回收的优点和原理。并考虑2种回收机制?
- 93、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
- 94、说出一些常用的类,包,接口,请各举5个?
- 95、java中实现多态的机制是什么?
- 96、下面哪些类可以被继承?
- 97、指出下面程序的运行结果(static代码段、构造方法执行顺序)
- 98、继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
- 99、关于内部类
- 100、数据类型之间的转换
- 101、写一个函数,要求输入一个字符串和一个字符长度,对该字符串进行分隔?
- 102、写一个函数,2个参数,1个字符串,1个字节数
- 103、日期和时间?
- 104、打印昨天的当前时刻?
- 105、java和javasciprt的区别?
- 106、什么时候用assert?
- 107、写出一个你最常见到的run time exception?
- 108、类ExampleA继承Exception,类ExampleB继承ExampleA?
- 109、介绍JAVA中的CollectionFrameWork(及如何写自己的数据结构)?
- 110、List,Set,Map是否继承自Collection接口?
- 111、你所知道的集合类都有哪些?主要方法?
- 112、说出ArrayList,Vector,LinkedList的存储性能和特性?
- 113、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?它们有何区别?
- 114、用程序给出随便大小的10个数,序号为1-10,按从小到大顺序输出,并输出相应的序号?
- 115、用JAVA实现一种排序,JAVA类实现序列化的方法?在COLLECTION框架中,实现比较要实现什么样的接口?
- 116、为什么HashMap链表长度超过8会转成树结构?
- 117、请说出你所知道的线程同步的方法?
- 118、创建线程的方式?
- 119、线程同步有几种实现方法,都是什么?
- 120、同步和异步有何异同,在什么情况下分别使用他们?
- 121、启动一个线程是用run()还是start()?
- 122、线程的基本概念、线程的基本状态以及状态之间的关系?
- 123、简述synchronized和java.util.concurrent.locks.Lock的异同?
- 124、用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
- 125、设计4个线程,其中两个线程每次对j增加1,另两个线程对j每次减少1写出程序?
- 126、什么是java序列化,如何实现java序列化?
- 127、java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
- 128、文件和目录(IO)操作?
- 129、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数?
- 130、用JAVASOCKET编程,读服务器几个字符,再写入本地显示?
- 131、什么是设计模式?分类是什么?
- 132、设计模式的六大原则?
- 133、写一个Singleton出来?
- 134、说说你所熟悉或听说过的JAVA EE中的几种常用模式?及对设计模式的一些看法?
- 135、Java中常用的设计模式?说明工厂模式?【中等难度】
- 136、开发中都用到了那些设计模式?用在什么场合?
- 137、你经常读那些书?
- 138、git,svn区别
- 139、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?
- 140、你在项目中用到了xml技术的哪些方面?如何实现的?
- 141、什么是重量级?什么是轻量级?
- 142、为什么要用clone?
- 143、new一个对象的过程和clone一个对象的过程区别
- 144、复制对象和复制引用的区别
- 145、深拷贝和浅拷贝
- 146、两个对象值相同(x.equals(y)==true),但却可有不同的hashCode,这句话对不对?
- 147、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
- 148、为什么函数不能根据返回类型来区分重载?
- 149、char型变量中能不能存储一个中文汉字,为什么?
- 150、请说出下面程序的输出
- 151、如何取得年月日、小时分钟秒?
- 152、如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
- 153、如何取得某月的最后一天?
- 154、如何格式化日期?
- 155、int和Integer有什么区别?
- 156、下面Integer类型的数值比较输出的结果为?
- 157、String类常用方法
- 158、Java中有几种类型的流
- 159、字节流如何转为字符流
- 160、如何将一个java对象序列化到文件里
- 161、字节流和字符流的区别
- 162、什么是java序列化,如何实现java序列化?
- 163、集合的安全性问题
- 164、ArrayList内部用什么实现的?
- 165、并发集合和普通集合如何区别?
- 166、List的三个子类的特点
- 167、List和Map、Set的区别
- 168、数组和链表的区别
- 169、Java中ArrayList和Linkedlist区别?
- 170、请用两个队列模拟堆栈结构
- 171、Collection和Map的集成体系
- 172、什么是线程池,如何使用?
- 173、常用的线程池有哪些?
- 174、请叙述一下您对线程池的理解?
- 175、说说你对Java中反射的理解
- 176、动静态代理的区别,什么场景使用?
- 177、写一个ArrayList的动态代理类
- 178、你所知道的设计模式有哪些
- 179、你对软件开发中迭代的含义的理解?
- 二、JavaWeb部分
- 1、四种会话跟踪技术
- 2、Jsp九大隐式对象
- 3、Servlet的生命周期
- 4、Servlet和Jsp的关系
- 5、什么是MVC?
- 6、重定向(redirect)和转发(forward)的异同点
- 7、Session和Cookie的区别
- 8、get和post的区别
- 9、写出浏览器JS的对象
- 10、简述在JSP中的注释有哪几种方法
- 11、JSP中动态INCLUDE与静态INCLUDE的区别?
- 12、尽量多的写出request下的常用方法
- 13、BS与CS的联系与区别。
- 14、Ajax的工作原理?
- 15、XMLHttpRequest对象是什么,它有哪些常用方法?
- 16、HTTP常见的状态码
- 17、数据库规范:三大范式
- 18、实体之间的关系
- 19、完整性约束
- 20、常用的端口
- 21、Servlet和Jsp的关系
- 22、Oracle中基本数据类型
- 23、JDBC访问数据库步骤
- 24、Ajax总结
- 25、Json(JavaScript Object Notation)描述
- 26、如何解决“Port 8080 is already in use”的问题
- 27、innerText和innerHtml的区别
- 28、`$(document).ready()`是个什么函数?为什么要用它?
- 29、JavaScript的window.onload事件和jQuery的ready函数有何不同
- 30、Jquery中有哪几种常用类型的选择器?
- 31、Jquery中如何实现节点遍历?
- 32、Jquery中实现Ajax语法?
- 33、Js和Jquery的关系
- 34、常见的应用服务器有那些?
- 35、MODEL1和MODEL2
- 36、在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?
- 37、http的长连接和短连接
- 38、http常见的状态码有哪些?
- 三、框架部分
- 1、IOC的概念
- 2、注入的三种方式
- 3、MyBatis的缓存机制
- 4、解释Spring支持的几种bean的作用域
- 5、在 Spring中如何注入一个java集合
- 6、动态代理设计模式
- 7、Spring中通知的类型
- 8、Hibernate框架工作原理
- 9、Hibernate对象的三种状态是什么
- 10、Hibernate对象的三种状态如何转换的
- 11、Hibernate分页怎样实现?
- 12、对象关系映射(ObjectRelationalMapping,简称ORM)
- 13、hibernate拒绝连接、服务器崩溃的原因?最少写5个
- 14、MyBatis与Hibernate有什么不同?
- 15、Struts2中result中的type类型
- 16、Struts2中访问ServletAPI三种方式:
- 17、struts2框架的核心控制器是什么?它有什么作用?
- 18、Hibernate中get和load之间的区别(重点):
- 19、Hibernate缓存概述
- 20、Hibernate核心类或接口
- 21、Hibernate配置文件中inverse属性作用
- 22、OpenSessionInView模式
- 23、Struts1和Struts2关系:
- 24、简述MyBatis框架
- 25、Spring中用到的设计模式
- 26、什么是bean自动装配?
- 27、解释自动装配的各种模式?
- 28、自动装配有哪些局限性?
- 29、Struts2中常见的拦截器:
- 30、Struts2中定义拦截器:
- 31、Spring事务的传播特性
- 32、Spring事务的隔离级别
- 33、hibernate实现多表关系的标签是什么?
- 34、Hibernate是如何处理事务的?
- 35、Hibernate的应用(Hibernate的结构)
- 36、什么是重量级?什么是轻量级?
- 37、什么是SpringMVC?简单介绍下你对springMVC的理解?
- 38、SpringMVC的流程?
- 39、SpringMVC的优点
- 40、SpringMVC的主要组件?
- 41、springMVC和struts2的区别有哪些?
- 42、SpringMVC怎么样设定重定向和转发的?
- 43、SpringMvc怎么和AJAX相互调用的?
- 44、如何解决POST请求中文乱码问题,GET的又如何处理呢?
- 45、Mybatis中`#{}`和`${}`的区别是什么?
- 46、通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
- 47、Mybatis是如何进行分页的?分页插件的原理是什么?
- 48、Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
- 49、Xml映射文件中,除了常见的`select|insert|update|delete`标签之外,还有哪些标签?
- 50、简述Mybatis的插件运行原理,以及如何编写一个插件
- 51、MyBatis一级、二级缓存
- 52、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?
- 53、简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系?
- 54、Spring是什么?
- 55、Spring的优点?
- 56、Spring的AOP理解
- 57、Spring的IoC理解:
- 58、解释Spring支持的几种bean的作用域。
- 59、Spring中bean的加载过程
- 60、Spring框架中的单例Beans是线程安全的么?
- 61、描述Struts2框架(工作原理)
- 62、Hibernate配置文件中CASECADE属性作用:
- 63、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
- 四、数据库部分
- 1、分页语句MySQL和Oracle
- 2、join与left join的区别:
- 3、SQL中where和having的区别
- 4、数据库三范式
- 5、说出你用过的常见的关系型数据库,非关系型数据库知道哪些?
- 6、存储过程和函数的区别?
- 7、事务是什么?
- 8、游标的作用?如何知道游标已经到了最后?
- 9、触发器?
- 10、有三张表,学生表S,课程C,学生课程表SC
- 11、数据库表(Test)结构如下
- 12、有如下两张表:表city:表state:
- 13、事务处理?
- 14、Java中访问数据库的步骤?Statement和PreparedStatement之间的区别?
- 15、用你熟悉的语言写一个连接ORACLE数据库的程序,能够完成修改和查询工作。
- 16、JDBC的实现分页
- 17、一条sql执行过长的时间,你如何优化,从哪些方面?
- 18、MySQL的事物隔离级别?
- 19、数据库连接池基本概念及原理
- 20、连接池技术点?
- 21、连接池的实现?
- 五、程序算法
- 1、杨辉三角
- 2、写一个函数计算当参数为N的值:1-2+3-4+5-6+7……+N
- 3、在ABCDEF六人中随机抽取3人中奖,要求中奖人不能重复
- 4、排序都有哪几种方法?请列举。用JAVA实现一个快速排序?
- 5、写一个一小段程序检查数字是否为质数;以上的程序你采用的哪种语言写的?采用该种语言的理由是什么?
- 6、设有n个人依围成一圈,从第1个人开始报数
- 7、写一个方法1000的阶乘
- 8、菲波拉契数列
- 9、素数
- 10、水仙花数
- 11、分解质因数
- 12、最大公约数和最小公倍数
- 13、输入一行字符分别统计出其中英文字母、空格、数字和其它字符的个数
- 14、完数
- 15、一球从100米高度自由落下,每次落地后反跳回原高度
- 16、一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
- 17、输入某年某月某日,判断这一天是这一年的第几天
- 18、输入三个整数x,y,z,请把这三个数由小到大输出
- 19、计算字符串中子串出现的次数
- 20、两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。
- 21、打印出如下图案(菱形)
- 22、有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。
- 23、求1+2!+3!+...+20!的和
- 24、利用递归方法求5!
- 25、有5个人坐在一起,问第五个人多少岁
- 26、给一个不多于5位的正整数
- 27、一个5位数,判断它是不是回文数
- 28、请输入星期几的第一个字母来判断一下是星期几
- 29、对10个数进行排序
- 30、求一个3*3矩阵对角线元素之和
- 31、有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
- 32、取一个整数a从右端开始的4~7位
- 33、求s=a+aa+aaa+aaaa+aa...a的值
- 34、打印出杨辉三角形(要求打印出10行如下图)
- 35、输入3个数a,b,c,按大小顺序输出
- 36、有1、2、3、4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位数?
- 37、企业发放的奖金根据利润提成
- 38、猴子吃桃问题
- 39、求100之内的素数
- 40、将一个数组逆序输出
- 六、JVM调优
- 七、Java优化
- 1、尽量指定类的final修饰符
- 2、尽量重用对象
- 3、尽量使用局部变量
- 4、不要重复初始化变量
- 5、操作中需要及时释放资源
- 6、保证过期对象的及时回收
- 7、在JAVA+ORACLE的应用系统开发中
- 8、在使用同步机制时,应尽量使用方法同步代替代码块同步。
- 9、尽量减少对变量的重复计算
- 10、尽量采用lazyloading的策略,即在需要的时候才开始创建
- 11、慎用异常
- 12、不要在循环中使用: Try{ }catch(){ } 应把其放置在最外层.
- 13、StringBuffer的使用
- 14、在JSP页面中关闭无用的会话
- 15、当复制大量数据时,使用System.arraycopy()命令。
- 16、代码重构:增强代码的可读性
- 17、不用new关键词创建类的实例
- 18、乘法和除法,用移位操作替代乘法操作可以极大地提高性能。
- 19、JDBC与I/O
- 20、Servlet与内存使用
- 21、使用缓冲标记
- 22、选择合适的引用机制
- 23、及时清除不再需要的会话
- 24、HashMap的遍历效率
- 25、不要将数组声明为:public static final
- 26、Array(数组)和ArryList的使用
- 27、尽量使用HashMap和ArrayList
- 28、优化GC的正确时机?
- 八、Redis相关
- 1、Redis简单介绍
- 2、Redis支持的数据类型
- 3、什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
- 4、redis通讯协议(RESP),能解释下什么是RESP?有什么特点?
- 5、Redis有哪些架构模式?讲讲各自的特点
- 6、什么是一致性哈希算法?什么是哈希槽?
- 7、Redis常用命令?
- 8、使用过Redis分布式锁么,它是怎么实现的?
- 9、使用过Redis做异步队列么,你是怎么用的?有什么缺点?
- 10、什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?
- 11、redis的事务处理
- 12、使用redis有哪些好处?
- 13、redis相比memcached有哪些优势?
- 14、redis常见性能问题和解决方案:
- 15、redis怎么和spring进行集成
- 16、redis内存数据集大小上升到一定大小的时候,就会执行数据淘汰策略。redis提供6种数据淘汰策略:
- 17、redis常见的性能问题都有哪些?如何解决?
- 18、分布式缓存
- 19、redis和memcached的区别
- 九、Nginx相关
- 十、WebService相关
- 十一、其他
一、JavaSE基础
1、接口和抽象类的异同点
1.1、相同
- 都不能创建对象
- 都可以定义抽象方法,并且一定要在子类中重写
1.2、不同
- 关键字不同abstract interface
- 抽象类中既可以有抽象的方法也可以有普通的方法
- 接口中中所有的方法都是抽象方法
- 抽象类的方法可以任意权限,接口中方法只能是public
- 抽象类只能单继承,接口可以多实现
2、重载(overload)和重写(overwrite)区别
2.1、重写的规则
子类和父类,子类重写了父类的方法
2.1.1、方法名、参数列表必须和父类完全一致
2.1.2、返回值类型要么相同,要么子类方法的返回值类型是父类方法返回值类型的子类!
/* 父类 */
public class Person {
public Object show(int age) {// 父类的方法返回值是Object
System.out.println("年龄是:" + age);
return age;
}
}
/* 子类 */
public class Student extends Person {
public Integer show(int age) {// 子类的方法继承父类的方法,但是返回值是Integer
System.out.println("年龄是:" + age);
return age;
}
}
2.1.3、访问修饰符要么相同,要么子类访问修饰符范围大于父类!
/* 父类 */
public class Person {
protected Object show(int age) {// 父类的方法访问修饰符是protected
System.out.println("年龄是:" + age);
return age;
}
}
/* 子类 */
public class Student extends Person {
public Integer show(int age) {// 子类的方法访问修饰符是public,大于父类
System.out.println("年龄是:" + age);
return age;
}
}
2.1.4、方法中抛出的异常,要么相同。要么子类方法抛出的异常比父类被重写方法抛出的异常更小或相同!
/* 父类 */
public class Person {
protected Object show(int age) throws Exception {// 父类的方法抛出的异常是Exception
System.out.println("年龄是:" + age);
return age;
}
}
/* 子类 */
public class Student extends Person {
public Integer show(int age) throws ClassCastException {// 子类的方法抛出的异常是ClassCastException,比父类小
System.out.println("年龄是:" + age);
return age;
}
}
2.2、重载的规则(两同一不同)
1)同一个类中
2)方法名相同
3)参数列表不同(个数,列表,类型)
4)和返回值无关
3、StringBuffer、StringBuilder、String三种类的区别
3.1、说明
- String字符串常量不可变使用字符串拼接时会开辟新空间。
- StringBuffer字符串变量可变线程安全字符串拼接直接在字符串后追加。
- StringBuilder字符串变量可变非线程安全字符串拼接直接在字符串后追加。
3.2、互相比较
- StringBuilder执行效率高StringBuffer,高于String。
- String是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象,StringBuffer和StringBuilder都是可变的,当进行字符串拼接时采用append方法,在原来的基础上进行追加,所以性能比String要高,StringBuffer是线程安全的而StringBuilder是线程非安全的,所以StringBuilder的效率高于StringBuffer。
- 对于大数据量的字符串的拼接,采用StringBuffer,StringBuilder。
3.3、性能比较
-
可变性:String是不可变的,即一旦创建就不能修改。而StringBuffer和StringBuilder是可变的,可以进行插入、删除、替换等操作。
-
线程安全性:String是线程安全的,即多个线程可以同时访问一个String对象而不会出现问题。而StringBuffer是线程安全的,可以在多线程环境下使用,而StringBuilder是非线程安全的,不适用于多线程环境。
-
性能:由于String是不可变的,每次对String进行修改都会创建一个新的String对象,因此在频繁修改字符串的情况下,使用String会产生大量的临时对象,导致性能下降。而StringBuffer和StringBuilder是可变的,不会创建新的对象,因此在频繁修改字符串时,使用它们的性能更好。StringBuilder的性能比StringBuffer更好,因为StringBuilder没有同步机制。
3.4、总结
综上所述,如果需要频繁修改字符串且在多线程环境下使用,应使用StringBuffer;如果在单线程环境下频繁修改字符串,应使用StringBuilder;如果字符串不需要修改,或者在多线程环境下共享字符串,应使用String。
4、冒泡排序(Bubble Sort)
冒泡排序(Bubble Sort)是最简单和最通用的排序方法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直至最终完成排序。由此可得,在排序过程中,大的数据往下沉,小的数据往上浮,就像气泡一样,于是将这种排序算法形象地称为冒泡排序。
public class BubbleSort {
// 冒泡排序
public static int[] bubbleSort(int arr[]) {
int temp;// 临时变量
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
public static void main(String[] args) {
// java定义一个整型的数组,声明数组时直接初始化其元素
int[] arrayToBeSorted = { 6, 3, 5, 2, 9, 1, 8, 7, 4 };
arrayToBeSorted = bubbleSort(arrayToBeSorted);
String resultString = "";
// java循环打印数组内容
for (int element : arrayToBeSorted) {
resultString = resultString + element + ", ";
}
// java字符串截取最后的“, ”字符
if (resultString.endsWith(", ")) {
resultString = resultString.substring(0, resultString.length() - 2);
}
System.out.println(resultString);
}
}
5、选择排序(Selection sort)
选择排序(Selection sort)是一种简单直观的排序算法。
它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
public class SelectionSort {
public static void main(String[] args) {
int[] a = { 25, 15, 42, 16, 12, 36 };
int max = 0;
int tmp = 0;
for (int i = 0; i < a.length; i++) {
max = i;
for (int j = i + 1; j < a.length; j++) {
if (a[max] < a[j])
max = j;// 记下较大数位置,再次比较,直到最大
}
if (i != max) {
tmp = a[i];
a[i] = a[max];
a[max] = tmp;
}
}
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}
6、单例设计模式
单例就是该类只能返回一个实例。
单例所具备的特点:
1.私有化的构造函数
2.私有的静态的全局变量
3.公有的静态的方法
在Java中,实现单例模式可以使用以下几种方式:
6.1、懒汉式
/* 懒汉式 */
public class Singleton {
// 静态属性指向唯一实例
private static Singleton uniqueInstance = null;
// 私有构造方法
private Singleton() {
}
// 提供唯一实例
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
6.2、线程安全懒汉式
/* 线程安全懒汉式 */
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {
}
// synchronized 线程安全的单例模式
public synchronized static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
6.3、饿汉式
/* 饿汉式 */
public class Singleton {
private Singleton() {
}
private static final Singleton uniqueInstance = new Singleton();
public static Singleton getInstance() {
return uniqueInstance;
}
}
7、常见的异常类型
NullPointerException空指针异常
ClassCastException类型强制转换异常
IllegalArgumentException传递非法参数异常
ArithmeticException算数运算异常
IndexOutOfBoundsException下标越界异常
NumberFormatException数字格式异常
ClassNotFindException加载请求异常
8、Throws和Throw的区别
- 位置:Throw方法内部,Throws方法名之后
- 作用:Throw抛出异常,Throws声明异常
- 个数:Throw一次抛出一个异常,Throws可以声明多个异常
public static void main(String[] args) throws ArithmeticException, Exception {
try {
int i = 100 / 0;
} catch (ArithmeticException e) {
throw new ArithmeticException("除数不能Θ");
} catch (Exception e) {
}
}
9、Final,Finally,finalize关键字的作用
- final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
- finally是异常处理语句结构的一部分,表示总是执行。不管是否有异常总是被执行,除非虚拟机停止才不执行。System.exit(1);
- finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法可以实现资源回收,释放资源,例如关闭文件等。JVM不保证此方法总被调用.
10、Final关键字的作用
- 修饰类:(最终类)不能被子类继承
- 修饰方法:(最终方法)不能被子类重写
- 修饰变量:(常量)一旦声明之后,不能再次修改其中的值
11、Hashtable与HashMap的区别
Hashmap允许空(null)键值(key),非线程安全,效率高。在多个线程访问时必须提供外同步。
HashMap是Java1.2引进的Map interface的一个实现。
Hashtable不允许key为null值,线程安全,但是效率低。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。
Hashtable和HashMap采用的hash/rehash算法大致一样,所以性能不会有很大的差异。
12、线程和进程的区别
线程是指在程序执行过程中,能够执行程序代码的一个基本执行单位,每个程序至少都有一个线程,也就是程序本身。
Java中的线程有四种状态:运行、就绪、挂起、结束。
进程是程序的基本执行实体,是线线程的容器。
进程具有的特征:
动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
并发性:任何进程都可以同其他进程一起并发执行;
独立性:进程是系统进行资源分配和调度的一个独立单位;
结构性:进程由程序、数据和进程控制块三部分组成。
线程和进程的区别:
1、隶属关系:线程是进程中的一部分,一个进程中可以包含若干个线程。进程可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。
2、地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
3、进程间通信:线程间可以直接读写进程数据段(如全局变量)来进行通信。需要进程同步和互斥手段的辅助,以保证数据的一致性。
4、调度和切换:线程上下文切换比进程上下文切换要快得多。
13、实现多线程程序的2种方式
多线程有两种实现方式:
一、继承Thread类。
二、实现Runnable接口。
同步的实现方法有两种:
Wait()使一个线程处于等待状态,并且释放所持有的对象的lock。
notify():唤醒一个处于等待状态的线程,在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,不是按优先级。
用synchronized关键字修饰同步方法反对使用stop(),是因为它不安全。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
14、List,Set,Collection,Collections
- 1.List和Set都是接口,他们都继承于接口Collection,List是一个有序的可重复的集合,而Set是无序的不可重复的集合。Collection是集合的顶层接口,Collections是一个封装了众多关于集合操作的静态方法的工具类,因为构造方法是私有的,所以不能实例化。
- 2.List接口实现类有ArrayList,LinkedList,Vector。ArrayList和Vector是基于数组实现的,所以查询的时候速度快,而在进行增加和删除的时候速度较慢LinkedList是基于链式存储结构,所以在进行查询的时候速度较慢但在进行增加和删除的时候速度较快。又因为Vector是线程安全的,所以他和ArrayList相比而言,查询效率要低。
15、sleep()和wait()有什么区别
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态
相同点在于都会造成线程阻塞.
16、error和exception有什么区别?
- error表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
- exception表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
17、heap(堆)和stack(栈)有什么区别
- java的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。
- 堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用new创建的对象都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用final修饰后,放在堆中,而不是栈中。
18、GC是什么?为什么要有GC?
- GC是垃圾收集的意思(GabageCollection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
19、内存泄漏和内存溢出
- 内存泄露(memoryleak),是指应用程序在申请内存后,无法释放已经申请的内存空间.一次内存泄露危害可以忽略,但如果任其发展最终会导致内存溢出(outofmemory).如读取文件后流要进行及时的关闭以及对数据库连接的释放。
- 内存溢出(outofmemory)是指应用程序在申请内存时,没有足够的内存空间供其使用。如我们在项目中对于大批量数据的导入,采用分段批量提交的方式。
20、运行时异常和checked异常的区别?
java异常是程序运行过程中出现的错误。
20.1、运行时异常
都是RuntimeException类及其子类异常。
如:
IndexOutOfBoundsException索引越界异常
ArithmeticException:数学计算异常
NullPointerException:空指针异常
ArrayOutOfBoundsException:数组索引越界异常
ClassCastException:造型异常(类型转换异常)
这些异常是不检查异常(UncheckedException),程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的。
20.2、checked异常
又叫做非运行时异常:是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
如:
IOException、文件读写异常
FileNotFoundException:文件未找到异常
EOFException:读写文件尾异常
MalformedURLException:URL格式错误异常
SocketException:Socket异常
SQLException:SQL数据库异常
21、四个访问修饰符合访问级别?
四个访问修饰符:private,default,protected,public
访问修饰符 | 本类 | 同包 | 子类 | 其他 |
---|---|---|---|---|
private | √ | |||
默认(friendly) | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
22、逻辑运算符:&和&&的区别?
运算符 | 含义 | 运算规则 |
---|---|---|
& && | 逻辑与 | 两个操作数都是true,结果才为true |
| || | 逻辑或 | 两个操作数一个是true,结果为true |
^ | 逻辑异或 | 两个操作数相同,结果为false |
两个操作数不同,结果为true | ||
^ | 逻辑反 | 操作数为true,结果为false |
操作数为false,结果为true |
-
&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
-
&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式。
-
&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作
23、Java中如何实现序列化,有什么意义
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆。
24、阐述JDBC操作数据库的步骤
- 1、加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
- 2、创建连接
Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","scott","tiger");
- 3、创建语句
PreparedStatement ps = con.prepareStatement("select * from emp where sal between ? and ?");
ps.setInt(1,1000);
ps.setInt(2,3000);
- 4、执行语句
ResultSet rs = ps.executeQuery();
- 5、处理结果
while(rs.next()){
System.out.println(rs.getInt("empno") + "-" + rs.getString("ename"));
}
- 5、关闭资源
finally{
if(con != null){
try{
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
提示:关闭外部资源的顺序应该和打开的顺序相反,也就是说先关闭ResultSet、再关闭Statement、在关闭Connection。上面的代码只关闭了Connection(连接),虽然通常情况下在关闭连接时,连接上创建的语句和打开的游标也会关闭,但不能保证总是如此,因此应该按照刚才说的顺序分别关闭。此外,第一步加载驱动在JDBC4.0中是可以省略的(自动从类路径中加载驱动)
25、Statement和PreparedStatement有什么区别?哪个性能更好?
与Statement相比:
- PreparedStatement接口代表预编译的语句,它主要的优势在于可以减少SQL的编译错误并增加SQL的安全性(减少SQL注射攻击的可能性);
- PreparedStatement中的SQL语句是可以带参数的,避免了用字符串连接拼接SQL语句的麻烦和不安全;
- 当批量处理SQL或频繁执行相同的查询时,PreparedStatement有明显的性能上的优势,由于数据库可以将编译优化后的SQL语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)。
26、二分查找法
-
1.二分查找又称折半查找,它是一种效率较高的查找方法。
-
2.二分查找要求(前提):(1)必须采用顺序存储结构(2).必须按关键字大小有序排列
-
3.原理:将数组分为三部分,依次是中值(所谓的中值就是数组中间位置的那个值)前,中值,中值后;将要查找的值和数组的中值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找,等于中值时直接返回。然后依次是一个递归过程,将前半部分或者后半部分继续分解为三部分。
-
4.代码实现
/*
* 循环实现二分查找算法。arr:已排好序的数组; x:需要查找的数;-1:无法查到数据
*/
public static int binarySearch(int[] arr, int x) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int middle = (low + high) / 2;
if (x == arr[middle]) {
return middle;
} else if (x < arr[middle]) {
high = middle - 1;
} else {
low = middle + 1;
}
}
return -1;
}
public static void main(String[] args) {
int[] array = { 1, 4, 7, 9, 10, 44, 78, 101, 203, 500 };
int index = binarySearch(array, 78);
System.out.println(index);
}
27、提示输入一个数,求阶乘
public static void main(String[] args) {
System.out.print("请输入一个正整数");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
if (n < 1) {
System.out.println("无效数据!");
return;
}
System.out.print(n + "!=");
int result = 1;
for (int i = 1; i <= n; i++) {
result = result * i;
if (i == n)
System.out.print(i + "=");
else
System.out.print(i + "x");
}
System.out.print(result);
}
28、斐波纳契数列
斐波纳契数列,又称黄金分割数列,数字上的体现是1,1,2,3,5,8,13……(实际上是前两个数之和等于第三个数)
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
int num = function(i);
System.out.println(num);
}
}
public static int function(int n) {
if (n == 1 || n == 2) {
return 1;
} else {
return function(n - 1) + function(n - 2);
}
}
29、Java中基本数据类型
数据类型 | 大小 | 包装类 |
---|---|---|
boolean(布尔型) | 1位 | Boolean(布尔型) |
byte(字节) | 1(8位) | Byte(字节) |
char(字符型) | 2(16位) | Character(字符型) |
shot(短整型) | 2(16位) | Shot(短整型) |
int(整型) | 4(32位) | Integer(整型) |
long(长整型) | 8(32位) | Long(整型) |
float(浮点型) | 4(32位) | Float(浮点型) |
double(双精度) | 8(64位) | Double(双精度) |
30、遍历D盘下面所有的文件
public static void testPrint(File file) {
String name = file.getName();
boolean b = file.isDirectory();// 判断是否为文件夹
if (b) {
// 是文件夹
File files[] = file.listFiles();
for (File f : files) {
testPrint(f);
// System.out.println(name);//打印文件夹名字
}
} else {
System.out.println(name);
}
}
public static void main(String[] args) {
File file = new File("d:\\");
Test4.testPrint(file);
}
31、ArrayList和LinkedList有什么区别
-
1)因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
-
2)相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
-
3)类似于插入数据,删除数据时,LinkedList也优于ArrayList。
-
4)LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
32、面向对象的特征有哪些方面
- 1、抽象:
抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。
抽象包括两个方面,一是过程抽象,二是数据抽象。
- 2、封装:
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。
面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
- 3、继承:
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。
对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。
- 4、多态:
多态是指允许不同类的对象对同一消息作出响应。
多态包括参数化多态性和包含多态性。
多态语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
33、String是最基本的数据类型吗?
- 基本数据类型包括byte、int、char、long、float、double、boolean和short。
- java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类。
34、有一个字符串,其中包含中文字符、英文字符和数字字符,请统计和打印出各个字符的个数
public static void main(String[] args) {
String str = "中国aadf的111萨bbb菲的zz萨菲";
int engishCount = 0;
int chineseCount = 0;
int digitCount = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch >= '0' && ch <= '9') {
digitCount++;
} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
engishCount++;
} else {
chineseCount++;
}
}
System.out.println("英文字符:" + engishCount);
System.out.println("中文字符:" + chineseCount);
System.out.println("数字字符:" + digitCount);
}
35、写一个程序将D盘下面的一张图片拷贝到E盘
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Date;
public class Test4 {
/*
* 从d盘将文件拷贝到e盘
*/
public static void main(String[] args) {
File f1 = new File("c:/2025年日历.png");
File f2 = new File("d:/2025年日历.png");
try {
forJava(f1, f2);
System.out.println("拷贝成功~");
} catch (Exception e) {
e.printStackTrace();
}
}
public static long forJava(File f1, File f2) throws Exception {
long time = new Date().getTime();
int length = 2097152;
FileInputStream in = new FileInputStream(f1);
FileOutputStream out = new FileOutputStream(f2);
byte[] buffer = new byte[length];
while (true) {
int ins = in.read(buffer);
if (ins == -1) {
in.close();
out.flush();
out.close();
return new Date().getTime() - time;
} else {
out.write(buffer, 0, ins);
}
}
}
}
36、静态类型有什么特点?
(1)静态的属性:随着类的加载而加载,该属性不在属于某个对象,属于整个类
(2)静态的方法:直接用类名调用,静态方法里不能访问非静态成员变量
(3)静态类:不能直接创建对象,不可被继承
37、说一下多态的表现形式?
(1)重载,重写,重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同
(2)重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法
38、线程通常有五种状态
(1)创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态
(2)就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
(3)运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
(4)阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
(5)死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪
39、事务的四大特性
事务必须具备以下四个属性,简称ACID 属性:
(1)原子性(Atomicity)
事务是一个完整的操作,事务的各步操作是不可分的(原子的),要么都执行,要么都不执行
(2)一致性(Consistency)
当事务完成时,数据必须处于一致状态
(3)隔离性(Isolation)
并发事务之间彼此隔离、独立,它不应以任何方式依赖于或影响其他事务
(4)永久性(Durability)
事务完成后,它对数据库的修改被永久保持
40、Java中IO体系
Java流的分类
按流向区分 | 输出流 | OutputStream 和 Writer 作为基类 |
输入流 | InputStream 和 Reader 作为基类 |
输入输出流是相对于计算机内存来说的
按照处理数据单元划分 | 字节流 | 字节输入流 InputStream 基类 |
字节输出流 OutputStream 基类 | ||
字符流 | 字符输入流 Reader 基类 | |
字符输出流 Writer 基类 |
字节流是 8 位通用字节流,字符流是 16 位 Unicode 字符流
IO流 | 字符流 | Reader | BufferedReader |
InputStreamReader | FileReader | ||
StringReader | |||
PipedReader | |||
ByteArrayReader | |||
FilterReader | PushbackReader | ||
Writer | BufferedWriter | ||
OutputStreamWriter | FileWriter | ||
PrinterWriter | |||
StringWriter | |||
PipedWriter | |||
CharArrayWriter | |||
FilterWriter | |||
字节流 | InputStream | FileInputStream | |
FilterInputStream | BufferedInputStream | ||
DataInputStream | |||
PushbackInputStream | |||
ObjectInputStream | |||
PipedInputStream | |||
SequenceInputStream | |||
StringBufferInputStream | |||
ByteArrayInputStream | |||
OutputStream | FileOutputStream | ||
FilterOutputStream | BufferedOutputStream | ||
DataOutputStream | |||
PrintStream | |||
ObjectOutputStream | |||
PipedOutputStream | |||
ByteArrayOutputStream |
41、字节流和字符流
- Stream结尾都是字节流,reader和writer结尾都是字符流
- 两者的区别就是读写的时候一个是按字节读写,一个是按字符,实际使用通常差不多。
- 在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。
- 只是读写文件,和文件内容无关的,一般选择字节流。
42、接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实体类(concreteclass)?
- 接口可以继承接口
- 抽象类可以实现接口
- 抽象类可以继承实体类
43、Class.forName的作用?为什么要用?
- 按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载过,则返回代表该字节码的Class实例对象,否则,按类加载器的委托机制去搜索和加载该类,如果所有的类加载器都无法加载到该类,则抛出ClassNotFoundException。加载完这个Class字节码后,接着就可以使用Class字节码的newInstance方法去创建该类的实例对象了。
- 有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行时才能确定,这时候就需要使用Class.forName去动态加载该类,这个类名通常是在配置文件中配置的,例如,Spring的ioc中每次依赖注入的具体类就是这样配置的,jdbc的驱动类名通常也是通过配置文件来配置的,以便在产品交付使用后不用修改源程序就可以更换驱动类名。
44、异常的体系结构
Java异常体系结构是Java编程语言中用于处理错误和异常情况的一种机制。它是通过类的继承关系来实现的,所有的异常类都继承自Throwable
类。
Java异常体系结构主要包括以下几个部分:
- Throwable类:
Throwable
是所有异常类的根类,它有两个直接子类:Error
和Exception
。Error
类表示严重的错误,一般由虚拟机或系统引发,程序一般无法处理。Exception
类表示一般的异常情况,可以由程序处理。 - Exception类:
Exception
类是所有一般异常的基类,它有很多子类,如RuntimeException
、IOException
等。RuntimeException
类及其子类是运行时异常,它们通常由程序逻辑错误引发,可以不显式地在代码中进行处理。其他异常类则是编译时异常,必须在代码中显式地进行处理。 - RuntimeException类:
RuntimeException
类及其子类是运行时异常,它们通常由程序逻辑错误引发,不需要显式地在代码中进行处理。常见的运行时异常包括NullPointerException
、ArrayIndexOutOfBoundsException
、ArithmeticException
等。 - IOException类:
IOException
类及其子类是输入输出异常,用于处理文件读写、网络通信等操作中可能出现的异常情况。 - 自定义异常:除了使用Java提供的异常类,开发者还可以根据自己的需求定义自己的异常类。自定义异常类一般继承自
Exception
或其子类。
在Java程序中,可以使用try-catch
语句块来捕获和处理异常。try
块用于包含可能引发异常的代码,catch
块用于处理捕获到的异常。如果try
块中的代码引发了异常,程序会跳转到与之匹配的catch
块进行异常处理。如果没有合适的catch
块处理异常,异常会继续向上层调用栈传递,直到找到合适的异常处理机制。
除了try-catch
语句块,还可以使用throws
关键字声明方法可能抛出的异常,将异常的处理责任交给调用者。
总结起来,Java异常体系结构提供了一种机制来处理程序中可能出现的错误和异常情况。开发者可以根据具体的需求选择合适的异常类进行处理,保证程序的稳定性和可靠性。
45、构造函数的特点和作用
- 方法名和类名相同
- 没有返回值,连void也没有
- 初始化对象
46、Java标识符的命名规则
- 只能有字母,数字,下划线和$组成
- 数字在不能开头
- 不能和关键字重名
- 见名知义
47、Java关键字
下面是Java的关键字列表:
关键字 | 描述 |
---|---|
abstract | 声明抽象类或抽象方法。 |
assert | 断言条件是否为真。 |
boolean | 声明布尔类型。 |
break | 跳出循环或switch语句。 |
byte | 声明字节类型。 |
case | 定义switch语句中的一个分支。 |
catch | 捕获异常。 |
char | 声明字符类型。 |
class | 声明类。 |
const | 保留字。不再使用。在早期版本的Java中,用于定义常量,但现在已经被final关键字替代。 |
continue | 继续下一次循环。 |
default | 定义switch语句中的默认分支。 |
do | 执行循环体,然后测试循环条件。 |
double | 声明双精度浮点数类型。 |
else | if语句的可选分支。 |
enum | 声明枚举类型。 |
extends | 扩展一个类。 |
false | 假 |
final | 声明一个不可改变的常量、类或方法。 |
finally | 有异常时,执行最后的清理操作。 |
float | 声明单精度浮点数类型。 |
for | 循环语句的开始。 |
goto | 保留字。不再使用。在Java中没有实际意义。 |
if | 条件语句的开始。 |
implements | 实现接口。 |
import | 引入其他包中的类。 |
instanceof | 测试对象是否为特定类型。 |
int | 声明整数类型。 |
interface | 声明接口。 |
long | 声明长整型数类型。 |
native | 标识方法使用非Java代码实现。 |
new | 创建新对象。 |
null | 空值 |
package | 声明包。 |
private | 声明私有属性或方法。 |
protected | 声明受保护的属性或方法。 |
public | 声明公共属性或方法。 |
return | 返回值。 |
short | 声明短整型数类型。 |
static | 声明静态属性或方法。 |
strictfp | 严格浮点计算。 |
super | 引用父类的成员或构造方法。 |
switch | 多分支选择语句。 |
synchronized | 同步代码块。 |
this | 当前对象的引用。 |
throw | 抛出异常。 |
throws | 声明可能抛出的异常。 |
transient | 指示属性不需要序列化。 |
true | 真 |
try | 捕获异常。 |
void | 声明无返回值的方法。 |
volatile | 指示属性可以被多个线程同时访问。 |
while | 循环语句的开始。 |
这些关键字在Java中具有特殊的含义,不能用作标识符或变量名。
48、构造函数的特点和作用
- 方法名和类名相同
- 没有返回值,连void也没有
- 初始化对象
49、synchronized关键字的用法?
- synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象){…}定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。
- 同步代码块
- 同步方法
50、启动一个线程是调用run()还是start()方法?
- 启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,这并不意味着线程就会立即运行。
- run()方法是线程启动后要进行回调(callback)的方法。
51、Swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
- 在Java5以前,switch(expr)中,expr只能是byte、short、char、int。从Java5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
System.out.println((int) '男'); // 30007
System.out.println((int) 'a'); // 97
System.out.println((int) 'A'); // 65
52、数组有没有length()方法?String有没有length()方法?
- 数组没有length()方法,有length的属性。String有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。
53、构造器(constructor)是否可被重写(override)?
- 构造器不能被继承,因此不能被重写,但可以被重载。
54、阐述静态变量和实例变量的区别
- 静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;
- 实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。
- 静态变量可以实现让多个对象共享内存。
- 注:在Java开发中,上下文类和工具类中通常会有大量的静态成员
55、比较一下Java和JavaSciprt
- JavaScript与Java是两个公司开发的不同的两个产品。
- Java是原SunMicrosystems公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。JavaScript的前身是LiveScript;而Java的前身是Oak语言。
- 下面对两种语言间的异同作如下比较:
- 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
- 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
- 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。
- 代码格式不一样。
56、JDK、JRE、JVM的区别
-
Jdk【JavaDevelopmentToolKit】就是java开发工具箱,JDK是整个JAVA的核心里边包含了jre,它除了包含jre之外还包含了一些javac的工具类,把java源文件编译成class文件,java文件是用来运行这个程序的,除此之外,里边还包含了java源生的API,java.lang.integer在rt的jar包里边【可以在项目中看到】,通过rt这个jar包来调用我们的这些io流写入写出等
-
JDK有以下三种版本:
- J2SE,standardedition,标准版,是我们通常用的一个版本
- J2EE,enterpsiseedtion,企业版,使用这种JDK开发J2EE应用程序
- J2ME,microedtion,主要用于移动设备、嵌入式设备上的java应用程序
-
Jre【JavaRuntimeEnviromental】是java运行时环境,那么所谓的java运行时环境,就是为了保证java程序能够运行时,所必备的一基础环境,也就是它只是保证java程序运行的,不能用来开发,而jdk才是用来开发的,所有的Java程序都要在JRE下才能运行。包括JVM和JAVA核心类库和支持文件。与JDK相比,它不包含开发工具——编译器、调试器和其它工具。Jre里边包含jvm
-
Jvm:【JavaVirtualMechinal】因为jre是java运行时环境,java运行靠什么运行,而底层就是依赖于jvm,即java虚拟机,java虚拟机用来加载类文件,java中之所以有跨平台的作用,就是因为我们的jvm
-
三者的关系:
- J2se是基于jdk和jre。
- JDK是整个JAVA的核心,里边包含了jre。
- Jre里边包含jvm。
57、XML和Json的特点
-
Xml特点:
1、有且仅有一个根节点;
2、是独立与软件和硬件的信息传输工具(传输量较大)
3、所有的标签都需要自定义
4、仅仅是纯文本文件 -
Json(JavaScriptObjectNotation)特点:
json分为两种格式:- json对象(就是在{}中存储键值对,键和值之间用冒号分隔,键值对之间用逗号分隔)。
- json数组(就是[]中存储多个json对象,json对象之间用逗号分隔)(两者间可以进行相互嵌套)
-
数据传输的载体之一
-
XML和Json两者的区别:
- xml的传输数据量比json的要大,流行的是基于json的数据传输。
-
XML和Json两者的共同点:
- Xml和json都是传输数据的载体,并且具有跨平台跨语言的特性。
58、JDK常用的包
- java.lang:这个是系统的基础类。比如String、Math、Integer、System和Thread,提供常用功能。java.lang包中还有一个子包:java.lang.reflect用于实现java类…。
- java.io:这里面是所有输入输出有关的类,比如文件操作等。
- java.net:这里面是与网络有关的类,比如URL,URLConnection等。
- java.util:这个是系统辅助类,特别是集合类Collection,List,Map等。
- java.sql:这个是数据库操作的类,Connection,Statememt,ResultSet等。
59、什么是值传递和引用传递?
- (1)值传递:形参类型是基本数据类型,方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形参值得改变不影响实际参数的值
- (2)引用传递:形参类型是引用数据类型参数,也称为传地址,方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留下来,所以方法执行中形式参数的改变将会影响实际参数。
60、解释一下数据库连接池
- 概念:一种关键的有限的昂贵的资源
- 影响因素:最大连接数,最小连接数
- 功能:分配、管理和释放数据库连接
- 作用:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个,释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
- 原理:连接池基本的思想是在系统初始化的时候,将数据库连接作为对象储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况。
61、空字符串(“”)和null的区别
- 空字符串是"“,会创建一个对象,内容是”",有内存空间。
- 而null,不会创建对象,没有内存空间
- 空字符串是String类的一个对象,而null是指一个引用变量没有引用对象,在值为null的引用变量上调用方法或变量,将会导致NullPointerException。通过以下代码来验证变量x是否为null,x==null,通过”".equals(x)来验证x是否为空字符串。
62、列举java中string类常用方法
- charAt(int index):返回指定索引处的char值。
- concat(String str):将指定字符串连接到此字符串的结尾。
- equals(Object anObject):比较字符串的内容是否相等。
- length():返回此字符串的长度。
- trim():用于去除字符串两端的空白字符(包括空格、制表符、换行符等)。它返回一个新的字符串,该字符串是原始字符串去除两端空白字符后的结果。trim()函数不会修改原始字符串,而是返回一个新的字符串对象。
- substring(int beginIndex):截取子串从beginindex开始。
- substring(int beginIndex,int endIndex):截取子串从哪到哪。
- split(String regex):分割字符串切割字符串。
- valueOf(float f):返回float参数的字符串表示形式。Float可以是任何java提供类型。
- toLowerCase:转成小写的与toUpperCase转成大写。
- compareTo(String anotherString):按字典顺序比较两个字符串。
- contains(CharSequence s):一个字符串中包不包含子串。
- indexOf(int ch):返回索引值第一次出现的位置。
- replace(char oldChar,char newChar):用newchar替换此字符串中出现的所有oldChar的值。
63、得到Class的三种方式是什么—反射
-
反射:是java中的一个机制,也叫反射机制。
反射:是一种Java编程语言的功能。它允许一个运行中的Java程序检查其本身,以及操作程序的内在属性。 -
第一种:通过每个对象都具备的方法getClass来获取。
-
第二种:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。.class用该属性就可以获取到字节码文件对象虽然不用对象调用,但还是要用具体的类调用静态属性。
-
第三种:使用的Class类中的方法,静态的forName方法。
例:
Student s=new Student();
Class stud=s.getClass(); // 1种
Class<Student> stu=Student.class; // 2种
Class forName=Class.forName("java.util.Data"); // 3种
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
64、什么是迭代器(Iterator)?
- Iterator提供了统一遍历操作集合元素的统一接口,Collection接口实现Iterable接口,每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例,然后对集合的元素进行迭代操作.有一点需要注意的是:在迭代元素的时候不能通过集合的方法删除元素,否则会抛出ConcurrentModificationException异常.但是可以通过Iterator接口中的remove()方法进行删除.
- Iterable接口
- Iteratoriterator();
- Iterator接口
- booleanhasNext();
- Enext();
- voidremove();
65、char型变量中能不能存贮一个中文汉字?为什么?
- char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
66、Java中compareTo和compare的区别?
- compareTo是Compareable接口的一个方法,主要用于规定创建对象的大小关系,该对象要实现compareable接口,当a.compareTo(b)>0时,则a>b,当a.compareTo(b)<0时,a<b.
- compare方法是java.util中的Comparator接口的一个方法,compare方法内主要靠定义compareTo规定的对象大小关系来确定对象的大小。
67、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?它们有何区别?
- Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。
- equals()和==方法决定引用值是否指向同一对象。equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
68、两个对像值相同(x.equals(y)==true),但却可有不同的hashcode,这句话对不对?
- 不对,有相同的hashcode。
69、Java的Socket通信(多线程)
Server | Client | |
---|---|---|
建立服务端倾听 socket | 建立连接 → | 创建连接socket 向服务端发送请求 |
等待并接收 连接请求 | ||
接收请求后 创建连接socket | ||
InputStream | → 开始通信 → | OutputStream |
OutputStream | InputStream | |
关闭socket及相关资源 | 结束通信→ | 关闭socket及相关资源 |
思路:
- 首先创建服务器端Socket,指定并侦听某一个端口,然后循环监听开始等待客户端的连接….
- 创建客户端socket,指定服务器地址和端口,然后获取输出流,向服务器端发送请求,并关闭socket输出流。
- 服务端接收到客户端的请求后,创建新线程并启动。
- 创建线程处理类,执行线程操作,获取输入流,服务端读取客户端用户详情,关闭资源。
- 执行线程操作,获取输出流,响应客户端请求,客户端接受到服务端的响应,关闭资源。
简单点讲,就相当于我跟你说话(客户端→服务端),你收到我说的话(服务端→线程处理类),大脑进行思考后(线程处理类),做出回答我的话(线程处理类→客户端)。
70、接口中定义
- 静态常量
- 抽象方法
public interface IFly {
public static int age = 100;
public abstract void showAge();
}
71、Break和Continue
- Break:退出当前本层循环
- Continue:跳过当前这次循环,继续下一次
hello: for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
for (int j2 = 0; j2 < 10; j2++) {
for (int k = 0; k < 10; k++) {
if (k == 0) {
break hello; // 退出多重循环
}
}
}
}
System.out.println(i);
}
System.out.println("程序结束");
72、什么是内部类?StaticNestedClass和InnerClass的不同。
- 内部类就是定义在另外一个类里面的类!
- StaticNestedClass(静态嵌套类)是InnerClass(内部类)的一种,被声明为静态(static)的内部类,因为是静态的,所以嵌套类要创建对象时,不需要外部类的对象,可以以Outer.newInner()创建对象静态嵌套类不能访问非静态外部类成员,而通常的内部类需要在外部类实例化后才能实例化。
73、下面这条语句一共创建了多少个对象:String S = “a”+“b”+”c”+”d”;
- 代码被编译器编译优化后,相当于直接定义了一个“abcd”的字符串,所以该语句只创建了一个对象
74、什么情况下finally中的代码不会执行?
- 如果在try块或者catch块中调用了退出虚拟机的方法(即System.exit();)那么finally中的代码不会执行,不然无论在try块、catch块中执行任何代码,出现任何情况,异常处理的finally中的代码都要被执行的。
75、一个“.java”源文件中是否可以包括多个类(不是内部类)?有什么限制?
-
一个“.java”源文件中可以包括多个类(不是内部类)!
-
单一个文件中只能有一个public类,并且该public类必须与文件名相同
76、Java有没有goto?
- goto是Java中的保留字(以后有可能会被启用变成关键字)
77、问题:如何将String类型转化成Number类型?
- Integer类的valueOf方法可以将String转成Number。
下面是代码示例:
String numString = "1000";
int id = Integer.valueOf(numString).intValue();
78、什么是隐式的类型转化?
- 隐式的类型转化就是简单的一个类型赋值给另一个类型,没有显式的告诉编译器发生了转化。并不是所有的类型都支持隐式的类型转化。
代码示例:
int i = 1000;
long j = i;// Implicitcasting
79、显式的类型转化是什么?
- 显式的类型转化是明确告诉了编译器来进行对象的转化。
代码示例:
long i = (long) 700.20;
int j = (int) i;// Explicitcasting
80、类型向下转换是什么?
- 向下转换是指由一个通用类型转换成一个具体的类型,在继承结构上向下进行。
81、如何原地交换两个变量的值?
81.1、先把两个值相加赋值给第一个变量,然后用得到的结果减去第二个变量,赋值给第二个变量。再用第一个变量减去第二个变量,同时赋值给第一个变量。
代码如下:
int a = 5, b = 10;
a = a + b;
b = a - b;
a = a - b;
System.out.println("a=" + a + ", b=" + b);
81.2、使用异或操作也可以交换。
第一个方法还可能会引起溢出。
异或的方法如下:
int a = 5, b = 10;
a = a ^ b;
System.out.println("a=" + a + ", b=" + b);
b = a ^ b;
System.out.println("a=" + a + ", b=" + b);
a = a ^ b;
System.out.println("a=" + a + ", b=" + b);
82、简述synchronized和java.util.concurrent.locks.Lock的异同?
- Lock是Java5以后引入的新的API,和关键字synchronized相比
- 主要相同点:Lock能完成synchronized所实现的所有功能;
- 主要不同点:
- Syschronized是在jvm层面上的,是一个关键字,而Lock是一个类;
- Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁;
- Syschronized同步数据少量的话,性能比Lock好,而数据大量同步,Lock性能要好
- synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally块中释放(这是释放外部资源的最好的地方)。
83、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
- sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态。
- wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lockpool),如果线程重新获得对象的锁就可以进入就绪状态。
84、线程的sleep()方法和yield()方法有什么区别?
- ①sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
- ②线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
- ③sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
- ④sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
85、Math.round(11.5)等于多少?Math.round(-11.5)等于多少?
- 答:Math.round(11.5)==12
- Math.round(-11.5)==-11
- round方法返回与参数最接近的长整数,参数加1/2后求其floor。
86、编程题:用最有效率的方法算出2乘以8等於几?
- 答:2<<3。
87、当一个对象被当作参数传递到方法,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
- 答:是值传递。Java编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。
88、定义类A和类B如下
class A {
int a = 1;
double d = 2.0;
void show() {
System.out.println("ClassA:a=" + a + "\td=" + d);
}
}
class B extends A {
float a = 3.0f;
String d = "Javaprogram.";
void show() {
super.show();
System.out.println("ClassB:a=" + a + "\td=" + d);
}
}
- (1)若在应用程序的main方法中有以下语句:
A a = new A();
a.show();
则输出的结果如何?
ClassA:a=1 d=2.0
- (2)若在应用程序的main方法中定义类B的对象b:
A b = new B();
b.show();
则输出的结果如何?
ClassA:a=1 d=2.0
ClassB:a=3.0 d=Javaprogram.
89、StaticNestedClass和InnerClass的不同?
- 答:StaticNestedClass是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
90、abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
- 答:都不能。
91、静态变量和实例变量的区别?
- 答:静态变量也称为类变量,归全类共有,它不依赖于某个对象,可通过类名直接访问;
- 而实例变量必须依存于某一实例,只能通过对象才能访问到它。
92、垃圾回收的优点和原理。并考虑2种回收机制?
- 答:Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
- 回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
93、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
- 答:对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。
- 可以。程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
94、说出一些常用的类,包,接口,请各举5个?
- 常用的类:BufferedReader、BufferedWriter、FileReader、FileWirter、String、Integer;
- 常用的包:java.lang、java.awt、java.io、java.util、java.sql;
- 常用的接口:Remote、List、Map、Document、NodeList;
95、java中实现多态的机制是什么?
- 方法的覆盖Overriding和重载Overloading是java多态性的不同表现;
- 覆盖Overriding是父类与子类之间多态性的一种表现,
- 重载Overloading是一个类中多态性的一种表现。
96、下面哪些类可以被继承?
1)java.lang.Thread(T)
2)java.lang.Number(T)
3)java.lang.Double(F)
4)java.lang.Math(F)
5)java.lang.Void(F)
6)java.lang.Class(F)
7)java.lang.ClassLoader(T)
答:1、2、7可以被继承。
97、指出下面程序的运行结果(static代码段、构造方法执行顺序)
class A {
static {
System.out.print("1");
}
public A() {
System.out.print("2");
}
}
class B extends A {
static {
System.out.print("a");
}
public B() {
System.out.print("b");
}
}
public class Hello {
public static void main(String[] args) {
A ab = new B(); // 执行到此处,结果:1a2b
ab = new B(); // 执行到此处,结果:1a2b2b
}
}
- 答:输出结果为1a2b2b;
- 类的static代码段,可以看作是类首次加载(虚拟机加载)执行的代码,而对于类加载,首先要执行其基类的构造,再执行其本身的构造。
98、继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
- 父类:
// 父类
public class FatherClass {
public FatherClass() {
System.out.println("FatherClassCreate");
}
}
- 子类:继承父类
//子类
public class ChildClass extends FatherClass {
public ChildClass() {
System.out.println("ChildClassCreate");
}
public static void main(String[] args) {
FatherClass fc = new FatherClass();
ChildClass cc = new ChildClass();
}
}
- 答:输出结果为:
FatherClassCreate
FatherClassCreate
ChildClassCreate
99、关于内部类
99.1、定义一个外部类
public class OuterClass {
private double d1 = 1.0;
// inner class code here
}
99.2、有下面5个内部类,看看选哪个好?
A:
class InnerOne {
public static double methoda() {
return d1;
}
}
B:
public class InnerOne {
static double methoda() {
return d1;
}
}
C:
private class InnerOne {
double methoda() {
return d1;
}
}
D:
static class InnerOne {
protected double methoda() {
return d1;
}
}
E:
abstract class InnerOne {
public abstract double methoda();
}
99.3、答案
答:答案为C、E;
99.4、解析
说明如下:
-
1)静态内部类可以有静态成员,而非静态内部类则不能有静态成员;故A、B错;
-
2)静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;故D错;
-
3)非静态内部类的非静态成员可以访问外部类的非静态变量;故C正确。
100、数据类型之间的转换
- 1)如何将数值型字符转换为数字?
- 2)如何将数字转换为字符?
- 3)如何取小数点前两位并四舍五入?
答:
- 1)调用数值类型相应包装类中的方法parse***(String)或valueOf(String)即可返回相应基本类型或包装类型数值;
- 2)将数字与空字符串相加即可获得其所对应的字符串;另外对于基本类型数字还可调用String类中的valueOf(…)方法返回相应字符串,而对于包装类型数字则可调用其toString()方法获得相应字符串;
- 3)可用该数字构造一java.math.BigDecimal对象,再利用其round()方法进行四舍五入到保留小数点后两位,再将其转换为字符串截取最后两位。
101、写一个函数,要求输入一个字符串和一个字符长度,对该字符串进行分隔?
代码如下:
public class TestSplit {
public static String[] split(String str, int chars) {
int n = (str.length() + chars - 1) / chars;
String ret[] = new String[n];
for (int i = 0; i < n; i++) {
if (i < n - 1) {
ret[i] = str.substring(i * chars, (i + 1) * chars);
} else {
ret[i] = str.substring(i * chars);
}
}
return ret;
}
public static void main(String[] args) {
String str = "asdffdvzxcvv";
int chars = 5;
String ret[] = split(str, chars);
for (String tmpString : ret) {
System.out.println(tmpString.toString());
}
}
}
102、写一个函数,2个参数,1个字符串,1个字节数
- 返回截取的字符串,要求字符串中的中文不能出现乱码:如(“我ABC”,4)应该截为“我AB”,输入(“我ABC汉DEF”,6)应该输出为“我ABC”而不是“我ABC+汉的半个”
代码如下:
/*
* 返回截取的字符串,要求:
* 1、字符串中的中文不能出现乱码:如(“我ABC”,4)应该截为“我AB”,
* 2、输入(“我ABC汉DEF”,6)应该输出为“我ABC”而不是“我ABC+汉的半个”
*/
public static String subString(String str, int subBytes) {
int bytes = 0;// 用来存储字符串的总字节数
for (int i = 0; i < str.length(); i++) {
if (bytes == subBytes) {
return str.substring(0, i);
}
char c = str.charAt(i);
if (c < 256) {
bytes += 1;// 英文字符的字节数看作1
} else {
bytes += 2;// 中文字符的字节数看作2
if (bytes - subBytes == 1) {
return str.substring(0, i);
}
}
}
return str;
}
103、日期和时间?
1)如何取得年月日、小时分秒?
2)如何取得从1970年到现在的毫秒数?
3)如何取得某个日期是当月的最后一天?
4)如何格式化日期?
答:
1)创建java.util.Calendar实例(Calendar.getInstance()),调用其get()方法传入不同的参数即可获得参数所对应的值,如:
calendar.get(Calendar.YEAR);//获得年
2)以下方法均可获得该毫秒数:
Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
3)示例代码如下:
Calendartime=Calendar.getInstance();
time.set(Calendar.DAY_OF_MONTH,
time.getActualMaximum(Calendar.DAY_OF_MONTH));
4)利用java.text.DataFormat类中的format()方法可将日期格式化。
104、打印昨天的当前时刻?
代码如下:
public class YesterdayCurrent {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
System.out.println(cal.getTime());
}
}
105、java和javasciprt的区别?
答:
JavaScript与Java是两个公司开发的不同的两个产品。Java是SUN公司推出的新一代面向对象的程序设计语言,特别适合于Internet应用程序开发;而JavaScript是Netscape公司的产品,其目的是为了扩展NetscapeNavigator功能,而开发的一种可以嵌入Web页面中的基于对象和事件驱动的解释性语言,它的前身是LiveScript;而Java的前身是Oak语言。
下面对两种语言间的异同作如下比较:
- 1)基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(ObjectBased)和事件驱动(EventDriver)的编程语言。因而它本身提供了非常丰富的内部对象供设计人员使用;
- 2)解释和编译:Java的源代码在执行之前,必须经过编译;JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行;
- 3)强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量声明,采用其弱类型。即变量在使用前不需作声明,而是解释器在运行时检查其数据类型;
- 4)代码格式不一样。
106、什么时候用assert?
答:
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。在实现中,断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式计算为false,那么系统会报告一个Assertionerror。
断言用于调试目的:
assert(a>0);//throwsanAssertionerrorifa<=0
断言可以有两种形式:
assertExpression1;
assertExpression1:Expression2;
Expression1应该总是产生一个布尔值。
Expression2可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的String消息。
断言在默认情况下是禁用的,要在编译时启用断言,需使用source1.4标记:
javac-source1.4Test.java
要在运行时启用断言,可使用-enableassertions或者-ea标记。
要在运行时选择禁用断言,可使用-da或者-disableassertions标记。
要在系统类中启用断言,可使用-esa或者-dsa标记。还可以在包的基础上启用或者禁用断言。可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。
107、写出一个你最常见到的run time exception?
答:
ArithmeticException,ArrayStoreException,BufferOverflowException,
BufferUnderflowException,CannotRedoException,CannotUndoException,
ClassCastException,CMMException,ConcurrentModificationException,
DOMException,EmptyStackException,IllegalArgumentException,
IllegalMonitorStateException,IllegalPathStateException,
IllegalStateException,ImagingOpException,IndexOutOfBoundsException,
MissingResourceException,NegativeArraySizeException,
NoSuchElementException,NullPointerException,ProfileDataException,
ProviderException,RasterFormatException,SecurityException,
SystemException,UndeclaredThrowableException,
UnmodifiableSetException,UnsupportedOperationException
108、类ExampleA继承Exception,类ExampleB继承ExampleA?
有如下代码片断:
public class TestException {
public static void main(String[] args) {
try {
throw new ExampleB("b");
} catch (ExampleA e) {
System.out.println("ExampleA");
} catch (Exception e) {
System.out.println("Exception");
}
}
}
class ExampleA extends Exception {
}
class ExampleB extends ExampleA {
public ExampleB(String string) {
}
}
输出的内容应该是:
A:ExampleA
B:Exception
C:b
D:无
答:输出为A。
109、介绍JAVA中的CollectionFrameWork(及如何写自己的数据结构)?
答:
CollectionFrameWork如下:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│└Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements);Map提供key到value的映射。
110、List,Set,Map是否继承自Collection接口?
答:
-
List、Set是;
-
Map不是。
111、你所知道的集合类都有哪些?主要方法?
答:ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
112、说出ArrayList,Vector,LinkedList的存储性能和特性?
答:ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
113、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals()?它们有何区别?
答:Set里的元素是不能重复的,用equals()方法来区分重复与否。覆盖equals()方法用来判断对象的内容是否相同,而”==”判断地址是否相等,用来决定引用值是否指向同一对象。
114、用程序给出随便大小的10个数,序号为1-10,按从小到大顺序输出,并输出相应的序号?
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class RandomSort {
public static void printRandomBySort() {
Random random = new Random();// 创建随机数生成器
List list = new ArrayList();
// 生成10个随机数,并放在集合list中
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(1000));
}
Collections.sort(list);// 对集合中的元素进行排序
Iterator it = list.iterator();
int count = 0;
while (it.hasNext()) {// 顺序输出排序后集合中的元素
System.out.println(++count + ":" + it.next());
}
}
public static void main(String[] args) {
printRandomBySort();
}
}
115、用JAVA实现一种排序,JAVA类实现序列化的方法?在COLLECTION框架中,实现比较要实现什么样的接口?
答:用插入法进行排序代码如下:
import java.util.ArrayList;
import java.util.Random;
public class InsertSort {
ArrayList al;
public InsertSort(int num, int mod) {
al = new ArrayList(num);
Random rand = new Random();
System.out.println("TheArrayListSortBefore:");
for (int i = 0; i < num; i++) {
al.add(new Integer(Math.abs(rand.nextInt()) % mod + 1));
System.out.println("al[" + i + "]=" + al.get(i));
}
}
public void SortIt() {
Integer tempInt;
int MaxSize = 1;
for (int i = 1; i < al.size(); i++) {
tempInt = (Integer) al.remove(i);
if (tempInt.intValue() >= ((Integer) al.get(MaxSize - 1)).intValue()) {
al.add(MaxSize, tempInt);
MaxSize++;
System.out.println(al.toString());
} else {
for (int j = 0; j < MaxSize; j++) {
if (((Integer) al.get(j)).intValue() >= tempInt.intValue()) {
al.add(j, tempInt);
MaxSize++;
System.out.println(al.toString());
break;
}
}
}
}
System.out.println("TheArrayListSortAfter:");
for (int i = 0; i < al.size(); i++) {
System.out.println("al[" + i + "]=" + al.get(i));
}
}
public static void main(String[] args) {
InsertSort is = new InsertSort(10, 100);
is.SortIt();
}
}
116、为什么HashMap链表长度超过8会转成树结构?
-
HashMap在JDK1.8及以后的版本中引入了红黑树结构,若桶中链表元素个数大于等于8时,链表转换成树结构;若桶中链表元素个数小于等于6时,树结构还原成链表。因为红黑树的平均查找长度是log(n),长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。
-
还有选择6和8,中间有个差值7可以有效防止链表和树频繁转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。
117、请说出你所知道的线程同步的方法?
答:
-
wait():使一个线程处于等待状态,并且释放所持有的对象的lock;sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常;notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级;
-
notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
118、创建线程的方式?
- 继承Thread类:创建一个继承自Thread类的子类,并重写其run()方法来定义线程的执行逻辑。然后通过创建子类的实例来创建线程对象,最后调用start()方法来启动线程。
public class MyThread extends Thread {
@Override
public void run() {
// 线程的执行逻辑
}
}
// 创建线程对象并启动线程
MyThread thread = new MyThread();
thread.start();
- 实现Runnable接口:创建一个实现了Runnable接口的类,并实现其run()方法来定义线程的执行逻辑。然后通过创建实现类的实例来创建线程对象,将其作为参数传递给Thread类的构造方法,最后调用start()方法来启动线程。
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程的执行逻辑
}
}
// 创建线程对象并启动线程
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
-
可以使用Callable和Future接口来创建线程。Callable接口是一个泛型接口,它定义了一个call()方法,可以在其中编写需要在新线程中执行的任务,并返回一个结果。与Runnable接口不同的是,Callable接口可以返回一个值,并且可以抛出一个异常。Future接口表示一个异步计算的结果。它提供了一些方法来检查计算是否完成,等待计算的完成,并获取计算的结果。
下面是一个使用Callable和Future创建线程的示例:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadExample {
public static void main(String[] args) {
// 创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(1);
// 创建一个Callable任务
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
// 在这里编写需要在新线程中执行的任务
return "Hello, World!";
}
};
// 提交任务并获取Future对象
Future<String> future = executor.submit(callable);
// 等待任务完成并获取结果
try {
String result = future.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
}
}
在上面的示例中,我们首先创建了一个线程池,然后创建了一个Callable任务,并将其提交给线程池。通过调用submit()方法,我们可以获取一个Future对象,它表示任务的异步计算结果。
然后,我们使用future.get()方法等待任务完成并获取结果。如果任务还没有完成,调用get()方法会阻塞当前线程,直到任务完成。
最后,我们关闭线程池,释放资源。
这样,我们就使用Callable和Future成功创建了一个线程,并获取了线程的执行结果。
119、线程同步有几种实现方法,都是什么?
答:
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
同步的实现方法有五种:
- 同步方法:
有synchronized关键字修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
- 同步代码块:
有synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步,同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可
- 使用特殊域变量(volatile)实现线程同步:
-
a.volatile关键字为域变量的访问提供了一种免锁机制,
-
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
-
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
-
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
- 使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
关于Lock对象和synchronized关键字的选择:
-
a.最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。
-
b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
-
c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
- 使用局部变量实现线程同步
使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响
120、同步和异步有何异同,在什么情况下分别使用他们?
答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
121、启动一个线程是用run()还是start()?
答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。
122、线程的基本概念、线程的基本状态以及状态之间的关系?
答:线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身;Java中的线程有四种状态分别是:运行、就绪、挂起、结束。
123、简述synchronized和java.util.concurrent.locks.Lock的异同?
答:主要相同点:Lock能完成synchronized所实现的所有功能;主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
124、用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
答:用synchronized关键字修饰同步方法;反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在;suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被“挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。故不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
125、设计4个线程,其中两个线程每次对j增加1,另两个线程对j每次减少1写出程序?
答:以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题:
public class TestThread {
private int j;
public TestThread(int j) {
this.j = j;
}
private synchronized void inc() {
j++;
System.out.println(j + "--Inc--" + Thread.currentThread().getName());
}
private synchronized void dec() {
j--;
System.out.println(j + "--Dec--" + Thread.currentThread().getName());
}
public void run() {
(new Dec()).start();
new Thread(new Inc()).start();
(new Dec()).start();
new Thread(new Inc()).start();
}
class Dec extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
dec();
}
}
}
class Inc implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
inc();
}
}
}
public static void main(String[] args) {
(new TestThread(5)).run();
}
}
126、什么是java序列化,如何实现java序列化?
答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题;序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需实现的方法,implementsSerializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Objectobj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
127、java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
答:字节流,字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io包中还有许多其他的流,主要是为了提高性能和使用方便。
128、文件和目录(IO)操作?
1)如何列出某个目录下的所有文件?
2)如何列出某个目录下的所有子目录?
3)如何判断一个文件或目录是否存在?
4)如何读写文件?
答:
1)示例代码如下:
File file = new File("d:\\test");
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isFile())
System.out.println(files[i]);
}
2)示例代码如下:
File file = new File("d:\\test");
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory())
System.out.println(files[i]);
}
3)创建File对象,调用其exsit()方法即可返回是否存在,如:
System.out.println(new File("d:\\tzq.txt").exists());
4)示例代码如下:
// 读文件:
FileInputStream fin = new FileInputStream("d:\\test\\tzqJava.txt");
byte[] bs = new byte[100];
while (true) {
int len = fin.read(bs);
if (len <= 0)
break;
System.out.print(new String(bs, 0, len));
}
fin.close();
// 写文件:
FileWriter fw = new FileWriter("d:\\test\\tzqJava.txt");
fw.write("helloworld!" + System.getProperty("line.separator"));
fw.write("XXX公司,JAVA欢迎你");
fw.close();
129、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数?
答:代码如下:
public static int countWords(String file, String find) throws Exception {
int count = 0;
Reader in = new FileReader(file);
int c;
while ((c = in.read()) != -1) {
while (c == find.charAt(0)) {
for (int i = 1; i < find.length(); i++) {
c = in.read();
if (c != find.charAt(i))
break;
if (i == find.length() - 1)
count++;
}
}
}
return count;
}
130、用JAVASOCKET编程,读服务器几个字符,再写入本地显示?
答:Server端程序:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private ServerSocket ss;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public Server() {
try {
ss = new ServerSocket(10000);
while (true) {
socket = ss.accept();
String RemoteIP = socket.getInetAddress().getHostAddress();
String RemotePort = ":" + socket.getLocalPort();
System.out.println("Aclient come in ! IP:" + RemoteIP + RemotePort);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = in.readLine();
System.out.println("Client send is:" + line);
out = new PrintWriter(socket.getOutputStream(), true);
out.println("Your Message Received!");
out.close();
in.close();
socket.close();
}
} catch (IOException e) {
out.println("wrong");
}
}
public static void main(String[] args) {
new Server();
}
}
Client端程序:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
Socket socket;
BufferedReader in;
PrintWriter out;
public Client() {
try {
System.out.println("Try to Connect to 127.0.0.1:10000");
socket = new Socket("127.0.0.1", 10000);
System.out.println("The Server Connected!");
System.out.println("Please enter some Character:");
BufferedReader line = new BufferedReader(new InputStreamReader(System.in));
out = new PrintWriter(socket.getOutputStream(), true);
out.println(line.readLine());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
out.close();
in.close();
socket.close();
} catch (IOException e) {
out.println("Wrong");
}
}
public static void main(String[] args) {
new Client();
}
}
131、什么是设计模式?分类是什么?
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
132、设计模式的六大原则?
132.1、开闭原则(OpenClosePrinciple)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
132.2、里氏代换原则(LiskovSubstitutionPrinciple)
里氏代换原则(LiskovSubstitutionPrincipleLSP)面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。——FromBaidu百科
132.3、依赖倒转原则(DependenceInversionPrinciple)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
132.4、接口隔离原则(InterfaceSegregationPrinciple)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
132.5、迪米特法则(最少知道原则)(DemeterPrinciple)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
132.6、合成复用原则(CompositeReusePrinciple)
原则是尽量使用合成/聚合的方式,而不是使用继承。
133、写一个Singleton出来?
答:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。举例:定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。
- 第一种形式:
public class Singleton {
private Singleton() {
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
- 第二种形式:
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
其他形式:定义一个类,它的构造函数为private的,所有方法为static的。一般认为第一种形式要更加安全些。
134、说说你所熟悉或听说过的JAVA EE中的几种常用模式?及对设计模式的一些看法?
答:
- SessionFacadePattern:使用SessionBean访问EntityBean;
- MessageFacadePattern:实现异步调用;
- EJBCommandPattern:使用CommandJavaBeans取代SessionBean,实现轻量级访问;
- DataTransferObjectFactory:通过DTOFactory简化EntityBean数据提供特性;
- GenericAttributeAccess:通过AttibuteAccess接口简化EntityBean数据提供特性;
- BusinessInterface:通过远程(本地)接口和Bean类实现相同接口规范业务逻辑一致性;
135、Java中常用的设计模式?说明工厂模式?【中等难度】
答:
- Java中的23种设计模式:Factory(工厂模式),Builder(建造模式),FactoryMethod(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式),Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式),Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),TemplateMethod(模板方法模式),ChainOfResponsibleity(责任链模式)。
- 工厂模式:工厂模式是一种经常被使用到的模式,根据工厂模式实现的类可以根据提供的数据生成一组类中某一个类的实例,通常这一组类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作。首先需要定义一个基类,该类的子类通过不同的方法实现了基类中的方法。然后需要定义一个工厂类,工厂类可以根据条件生成不同的子类实例。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
136、开发中都用到了那些设计模式?用在什么场合?
答:
- 每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。主要用到了MVC的设计模式,用来开发JSP/Servlet或者J2EE的相关应用;及简单工厂模式等。
137、你经常读那些书?
Java编程思想、Java模式、人月神话…
138、git,svn区别
Git是分布式的,SVN是集中式的,好处是跟其他同事不会有太多的冲突,自己写的代码放在自己电脑上,一段时间后再提交、合并,也可以不用联网在本地提交;
139、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?
答:
1)两种形式:dtd以及schema;
2)本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的);
3)解析方式:有DOM,SAX,STAX、DOM4J等:
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问;SAX:不同于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问;
STAX:StreamingAPIforXML(StAX)。
140、你在项目中用到了xml技术的哪些方面?如何实现的?
答:
用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。
141、什么是重量级?什么是轻量级?
答:轻量级是指它的创建和销毁不需要消耗太多的资源,意味着可以在程序中经常创建和销毁session的对象;重量级意味不能随意的创建和销毁它的实例,会占用很多的资源。
142、为什么要用clone?
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。
143、new一个对象的过程和clone一个对象的过程区别
new操作符的本意是分配内存。程序执行到new操作符时,首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化。
构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和原对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
144、复制对象和复制引用的区别
144.1、复制引用
Person p = new Person(23,"zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
当Person p1 = p;
执行之后,是创建了一个新的对象吗?首先看打印结果:
1.com.bruceliu.Person@2f9ee1ac
2.com.bruceliu.Person@2f9ee1ac
可以看出,打印的地址值是相同的,既然地址都是相同的,那么肯定是同一个对象。p和p1只是引用而已,他们都指向了一个相同的对象Person(23,“zhang”)。可以把这种现象叫做引用的复制。上面代码执行完成之后,内存中的情景如下图所示:
p | → | ... | → | 23 zhang |
p1 | → | ... | → | |
栈区(引用) | ... | 栈区(对象) |
144.2、复制对象
而下面的代码是真真正正的克隆了一个对象。
Person p = new Person(23,"zhang");
Person p1 = (Person)p.clone();
System.out.println(p);
System.out.println(p1);
从打印结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象,而不是把原对象的地址赋给了一个新的引用变量:
1.com.bruceliu.Person@2f9ee1ac
2.com.bruceliu.Person@67f1fba0
以上代码执行完成后,内存中的情景如下图所示:
p | →→→→ | ... | →→→→ | 23 zhang | |
p | →→→→ | ... | →→→→ | →→→→ | 23 zhang |
栈区(引用) | ... | 栈区(对象) |
145、深拷贝和浅拷贝
上面的示例代码中,Person中有两个成员变量,分别是name和age,name是String类型,age是int类型。代码非常简单,如下所示:
public class Person implements Cloneable {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
由于age是基本数据类型,那么对它的拷贝没有什么意义,直接将一个4字节的整数值拷贝过来就行。但是name是String类型的,它只是一个引用,指向一个真正的String对象,那么对它的拷贝有两种方式:直接将原对象中的name的引用值拷贝给新对象的name字段,或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。
下面通过代码进行验证。如果两个Person对象的name的地址值相同,说明两个对象的name都指向同一个String对象,也就是浅拷贝,而如果两个对象的name的地址值不同,那么就说明指向不同的String对象,也就是在拷贝Person对象的时候,同时拷贝了name引用的String对象,也就是深拷贝。验证代码如下:
public static void main(String[] args) throws Exception {
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
String result = p.getName() == p1.getName() ? "clone是浅拷贝的" : "clone是深拷贝的";
System.out.println(result);
}
打印结果为:clone是浅拷贝的。
所以,clone方法执行的是浅拷贝,在编写程序时要注意这个细节。
如何进行深拷贝:
由上内容可以得出如下结论:如果想要深拷贝一个对象,这个对象必须要实现Cloneable接口,实现clone方法,并且在clone方法内部,把该对象引用的其他对象也要clone一份,这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。那么,按照上面的结论,实现以下代码Body类组合了Head类,要想深拷贝Body类,必须在Body类的clone方法中将Head类也要拷贝一份。代码如下:
public class Maintest {
static class Body implements Cloneable {
public Head head;
public Body() {
}
public Body(Head head) {
this.head = head;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable {
public Head() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body==body1:" + (body == body1));
System.out.println("body.head==body1.head:" + (body.head == body1.head));
}
}
打印结果为:
body==body1:false
body.head==body1.head:false
146、两个对象值相同(x.equals(y)==true),但却可有不同的hashCode,这句话对不对?
- 不对,如果两个对象x和y满足x.equals(y)==true,它们的哈希码(hashCode)应当相同。
- Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
- 关于equals和hashCode方法,很多Java程序员都知道,但很多人也就是仅仅知道而已,在JoshuaBloch的大作《EffectiveJava》(很多软件公司,《EffectiveJava》、《Java编程思想》以及《重构:改善既有代码质量》是Java程序员必看书籍,如果你还没看过,那就赶紧去买一本吧)中是这样介绍equals方法的。
- 首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。
实现高质量的equals方法的诀窍包括:
- 使用==操作符检查"参数是否为这个对象的引用";
- 使用instanceof操作符检查"参数是否为正确的类型";
- 对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;
- 编写完equals方法后,问自己它是否满足对称性、传递性、一致性;
- 重写equals时总是要重写hashCode;
- 不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。
147、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和C#中可以通过传引用或传输出参数来改变传入的参数的值。说明:Java中没有传引用实在是非常的不方便,这一点在Java8中仍然没有得到改进,正是如此在Java编写的代码中才会出现大量的Wrapper类(将需要通过方法调用修改的引用置于一个Wrapper类中,再将Wrapper对象传入方法),这样的做法只会让代码变得臃肿,尤其是让从C和C++转型为Java程序员的开发者无法容忍。
148、为什么函数不能根据返回类型来区分重载?
该道题来自华为面试题。
因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。例如:
当调用max(1,2);时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。
再比如对下面这两个方法来说,虽然它们有同样的名字和自变量,但其实是很容易区分的:
若编译器可根据上下文(语境)明确判断出含义,比如在intx=f()中,那么这样做完全没有问题。然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法”,因为我们关心的不是返回值,而是方法调用的其他效果。所以假如我们像下面这样调用方法:f();Java怎样判断f()的具体调用方式呢?而且别人如何识别并理解代码呢?由于存在这一类的问题,所以不能。
函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。并不能作为某个方法的“标识”。
149、char型变量中能不能存储一个中文汉字,为什么?
char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个中文是没问题的。
补充:使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以Java中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于C程序员来说,要完成这样的编码转换恐怕要依赖于union(联合体/共用体)共享内存的特征来实现了。
150、请说出下面程序的输出
public static void main(String[] args) throws Exception {
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2);// false
System.out.println(s1 == s5);// true
System.out.println(s1 == s6);// false
System.out.println(s1 == s6.intern());// true
System.out.println(s2 == s2.intern());// false
}
在Java中,String类的intern()函数用于将字符串对象添加到字符串常量池中,并返回常量池中对应的引用。字符串常量池是Java中用于存储字符串字面值的一个特殊的内存区域。
当调用intern()函数时,如果常量池中已经存在该字符串,则返回常量池中的引用;如果常量池中不存在该字符串,则将该字符串添加到常量池中,并返回常量池中的引用。
使用intern()函数可以实现字符串的共享,节省内存空间。当多个字符串对象具有相同的内容时,可以使用intern()函数将它们指向同一个字符串常量,避免重复创建相同内容的字符串对象。
需要注意的是,使用intern()函数可能会增加字符串对象的比较时间,因为它需要在常量池中进行查找。因此,只有在需要比较大量字符串对象的内容时,才建议使用intern()函数。
151、如何取得年月日、小时分钟秒?
public static void main(String[] args) throws Exception {
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));// 0-11
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
// Java8
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.getYear());
System.out.println(dt.getMonthValue());// 1-12
System.out.println(dt.getDayOfMonth());
System.out.println(dt.getHour());
System.out.println(dt.getMinute());
System.out.println(dt.getSecond());
}
输出结果:
2024
11
23
9
36
4
2024
12
23
9
36
4
152、如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
public static void main(String[] args) throws Exception {
long l1 = Calendar.getInstance().getTimeInMillis(); // 第一种方式
long l2 = System.currentTimeMillis(); // 第二种方式
// Java8
long l3 = Clock.systemDefaultZone().millis(); // 第三种方式
System.out.println(l1);
System.out.println(l2);
System.out.println(l3);
}
153、如何取得某月的最后一天?
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
public class Test8 {
public static void main(String[] args) throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 获取当前月第一天:
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 0);
c.set(Calendar.DAY_OF_MONTH, 1);// 设置为1号,当前日期既为本月第一天
String first = format.format(c.getTime());
System.out.println("===============first:" + first);
// 获取当前月最后一天
Calendar ca = Calendar.getInstance();
ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
String last = format.format(ca.getTime());
System.out.println("===============last:" + last);
// Java8
LocalDate today = LocalDate.now();
// 本月的第一天
LocalDate firstday = LocalDate.of(today.getYear(), today.getMonth(), 1);
// 本月的最后一天
LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("本月的第一天:" + firstday);
System.out.println("本月的最后一天:" + lastDay);
}
}
执行结果:
===============first:2024-12-01
===============last:2024-12-31
本月的第一天:2024-12-01
本月的最后一天:2024-12-31
154、如何格式化日期?
-
Java.text.DataFormat的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。
-
Java8中可以用java.time.format.DateTimeFormatter来格式化时间日期,
代码如下所示:
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
public class Test8 {
public static void main(String[] args) throws Exception {
SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
Date date1 = new Date();
System.out.println(oldFormatter.format(date1));
// Java8
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}
}
执行结果:
2024/12/23
2024/12/23
-
补充:Java的时间日期API一直以来都是被诟病的东西,为了解决这一问题,Java8中引入了新的时间日期API。
其中包括LocalDate、LocalTime、LocalDateTime、Clock、Instant等类,这些的类的设计都使用了不变模式,因此是线程安全的设计。
155、int和Integer有什么区别?
- Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapperclass),int的包装类就是Integer,从Java5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java为每个原始类型提供了包装类型:
-
原始类型:boolean,char,byte,short,int,long,float,double
-
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
public class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成 Integer 类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成 int 类型再和c比较
}
}
156、下面Integer类型的数值比较输出的结果为?
public class Test9 {
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2);
System.out.println(f3 == f4);
}
}
输出结果:
true
false
- 如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。
Integer valueOf()
源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache是Integer的内部类,其代码如下所示:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
- 简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1f2的结果是true,而f3f4的结果是false。
157、String类常用方法
方法 | 说明 |
---|---|
int length() | 返回当前字符串的长度 |
int indexOf(int ch) | 查找ch字符在该字符串中第一次出现的位置 |
int indexOf(String str) | 查找str子字符串在该字符串中第一次出现的位置 |
int lastIndexOf(int ch) | 查找ch字符在该字符串中最后一次出现的位置 |
int lastIndex0f(String str) | 查找str子字符串在该字符串中最后一次出现的位置 |
String substring(int beginIndex) | 获取从beginIndex位置开始到结束的子字符串 |
String substring(int beginIndex, int endIndex) | 获取从beginIndex位置开始到endIndex位置的子字符串 |
String trim() | 返回去除了前后空格的字符串 |
boolean equals(0bject obj) | 将该字符串与指定对象比较,返回true或false |
String toLowerCase() | 将字符串转换为小写 |
String toUpperCase() | 将字符串转换为大写 |
char charAt(int index) | 获取字符串中指定位置的字符 |
String[] split(String regex, int limit) | 将字符串分割为子字符串,返回字符串数组 |
byte[] getBytes() | 将该字符串转换为byte数组 |
158、Java中有几种类型的流
按照流的方向:输入流(inputStream)和输出流(outputStream)。
按照实现功能分:节点流(可以从或向一个特定的地方(节点)读写数据。如FileReader)和处理流(是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。)
按照处理数据的单位:字节流和字符流。字节流继承于InputStream和OutputStream,字符流继承于InputStreamReader和OutputStreamWriter。
159、字节流如何转为字符流
字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象。
字节输出流转字符输出流通过OutputStreamWriter实现,该类的构造函数可以传入OutputStream对象。
160、如何将一个java对象序列化到文件里
在java中能够被序列化的类必须先实现Serializable接口,该接口没有任何抽象方法只是起到一个标记作用。
字节流和字符流的区别 public static void main(String[] args) {
// 对象输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("D://obj")));
objectOutputStream.writeObject(new User("zhangsan", 100));
objectOutputStream.close();
// 对象输入流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("D://obj")));
User user = (User) objectInputStream.readObject();
System.out.println(user);
objectInputStream.close();
}
161、字节流和字符流的区别
-
字节流读取的时候,读到一个字节就返回一个字节;字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8码表中是3个字节)时。先去查指定的编码表,将查到的字符返回。字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。字节流主要是操作byte类型数据,以byte数组为准,主要操作类就是OutputStream、InputStream。
-
字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。在程序中一个字符等于两个字节,java提供了Reader、Writer两个专门操作字符流的类。
162、什么是java序列化,如何实现java序列化?
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implementsSerializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Objectobj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
163、集合的安全性问题
请问ArrayList、HashSet、HashMap是线程安全的吗?如果不是我想要线程安全的集合怎么办?
我们都看过上面那些集合的源码(如果没有那就看看吧),每个方法都没有加锁,显然都是线程不安全的。话又说过来如果他们安全了也就没第二问了。
在集合中Vector和HashTable倒是线程安全的。你打开源码会发现其实就是把各自核心方法添加上了synchronized关键字。
Collections工具类提供了相关的API,可以让上面那3个不安全的集合变为安全的。
1.// Collections.synchronizedCollection©
2.// Collections.synchronizedList(list)
3.// Collections.synchronizedMap(m)
4.// Collections.synchronizedSet(s)
上面几个函数都有对应的返回值类型,传入什么类型返回什么类型。打开源码其实实现原理非常简单,就是将集合的核心方法添加上了synchronized关键字。
164、ArrayList内部用什么实现的?
(回答这样的问题,不要只回答个皮毛,可以再介绍一下ArrayList内部是如何实现数组的增加和删除的,因为数组在创建的时候长度是固定的,那么就有个问题我们往ArrayList中不断的添加对象,它是如何管理这些数组呢?)
ArrayList内部是用Object[]实现的。接下来我们分别分析ArrayList的构造、add、remove、clear方法的实现原理。
一、构造函数
1.1、空参构造
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- elementData是一个Object[]类型。当我们new一个空参构造时系统调用了DEFAULTCAPACITY_EMPTY_ELEMENTDATA属性,DEFAULTCAPACITY_EMPTY_ELEMENTDATA仅仅是一个系统的类库,也就是说当我们new一个空参ArrayList的时候,系统内部使用了一个Object[]数组常量。
1.2、带参构造1
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
- 该构造函数传入一个int值,该值作为数组的长度值。如果该值小于0,则抛出一个运行时异常。如果等于0,则使用一个空数组,如果大于0,则创建一个长度为该值的新数组。
1.3、带参构造2
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
-
如果调用构造函数的时候传入了一个Collection的子类,那么先判断该集合是否为null,为null则抛出空指针异常。如果不是则将该集合转换为数组a,然后将该数组赋值为成员变量array,将该数组的长度作为成员变量size。这里面它先判断a.getClass是否等于Object[].class,其实一般都是相等的,我也暂时没想明白为什么多加了这个判断,
-
toArray方法是Collection接口定义的,因此其所有的子类都有这样的方法,list集合的toArray和Set集合的toArray返回的都是Object[]数组。
-
这里讲些题外话,其实在看Java源码的时候,作者的很多意图都很费人心思,我能知道他的目标是啥,但是不知道他为何这样写。比如对于ArrayList,array是他的成员变量,但是每次在方法中使用该成员变量的时候作者都会重新在方法中开辟一个局部变量,然后给局部变量赋值为array,然后再使用,有人可能说这是为了防止并发修改array,毕竟array是成员变量,大家都可以使用因此需要将array变为局部变量,然后再使用,这样的说法并不是都成立的,也许有时候就是老外们写代码的一个习惯而已。
二、add方法
- add方法有两个重载,这里只研究最简单的那个。
/* 计算容量 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/* 确保显式容量 */
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/* 确保容量内部 */
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- 可以看到,先确保容量后,再把对象加到元素数据数组中去。
三、remove方法
- remove方法有两个重载,我们只研究remove(intindex)方法。
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
1、先将成员变量array和size赋值给局部变量a和s。
2、判断形参index是否大于等于集合的长度,如果成了则抛出运行时异常
3、获取数组中脚标为index的对象result,该对象作为方法的返回值
4、调用System的arraycopy函数,
5、接下来就是很重要的一个工作,因为删除了一个元素,而且集合整体向前移动了一位,因此需要将集合最后一个元素设置为null,否则就可能内存泄露。
6、重新给成员变量array和size赋值
7、记录修改次数
8、返回删除的元素(让用户再看最后一眼)
四、clear方法
- 如果集合长度不等于0,则将所有数组的值都设置为null,然后将成员变量size设置为0即可,最后让修改记录加1。
165、并发集合和普通集合如何区别?
并发集合常见的有ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque等。并发集合位于java.util.concurrent包下,是jdk1.5之后才有的,主要作者是DougLea
(http://baike.baidu.com/view/3141057.htm)完成的。
在java中有普通集合、同步(线程安全)的集合、并发集合。普通集合通常性能最高,但是不保证多线程的安全性和并发的可靠性。线程安全集合仅仅是给集合添加了synchronized同步锁,严重牺牲了性能,而且对并发的效率就更低了,并发集合则通过复杂的策略不仅保证了多线程的安全又提高的并发时的效率。
参考阅读:
ConcurrentHashMap是线程安全的HashMap的实现,默认构造同样有initialCapacity和loadFactor属性,不过还多了一个concurrencyLevel属性,三属性默认值分别为16、0.75及16。其内部使用锁分段技术,维持这锁Segment的数组,在Segment数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。
put操作:并没有在此方法上加上synchronized,首先对key.hashcode进行hash操作,得到key的hash值。hash操作的算法和map也不同,根据此hash值计算并获取其对应的数组中的Segment对象(继承自ReentrantLock),接着调用此Segment对象的put方法来完成当前操作。
ConcurrentHashMap基于concurrencyLevel划分出了多个Segment来对key-value进行存储,从而避免每次put操作都得锁住整个数组。在默认的情况下,最佳情况下可允许16个线程并发无阻塞的操作集合对象,尽可能地减少并发时的阻塞现象。
get(key)
首先对key.hashCode进行hash操作,基于其值找到对应的Segment对象,调用其get方法完成当前操作。而Segment的get操作首先通过hash值和对象数组大小减1的值进行按位与操作来获取数组上对应位置的
HashEntry。在这个步骤中,可能会因为对象数组大小的改变,以及数组上对应位置的HashEntry产生不一致性,那么ConcurrentHashMap是如何保证的?
对象数组大小的改变只有在put操作时有可能发生,由于HashEntry对象数组对应的变量是volatile类型的,因此可以保证如HashEntry对象数组大小发生改变,读操作可看到最新的对象数组大小。
在获取到了HashEntry对象后,怎么能保证它及其next属性构成的链表上的对象不会改变呢?这点ConcurrentHashMap采用了一个简单的方式,即HashEntry对象中的hash、key、next属性都是final的,这也就意味着没办法插入一个HashEntry对象到基于next属性构成的链表中间或末尾。这样就可以保证当获取到HashEntry对象后,其基于next属性构建的链表是不会发生变化的。
ConcurrentHashMap默认情况下采用将数据分为16个段进行存储,并且16个段分别持有各自不同的锁
Segment,锁仅用于put和remove等改变集合对象的操作,基于volatile及HashEntry链表的不变性实现了读取的不加锁。这些方式使得ConcurrentHashMap能够保持极好的并发支持,尤其是对于读远比插入和删除频繁的Map而言,而它采用的这些方法也可谓是对于Java内存模型、并发机制深刻掌握的体现。
166、List的三个子类的特点
ArrayList:底层结构是数组,底层查询快,增删慢。
LinkedList:底层结构是链表型的,增删快,查询慢。
voctor:底层结构是数组线程安全的,增删慢,查询慢。
167、List和Map、Set的区别
结构特点
List和Set是存储单列数据的集合,Map是存储键和值这样的双列数据的集合;List中存储的数据是有顺序,并且允许重复;Map中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的,Set中存储的数据是无序的,且不允许有重复,但元素在集合中的位置由元素的hashcode决定,位置是固定的(Set集合根据hashcode来进行数据的存储,所以位置是固定的,但是位置不是用户可以控制的,所以对于用户来说set中的元素还是无序的);
实现类
List接口有三个实现类(LinkedList:基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢;ArrayList:基于数组实现,非线程安全的,效率高,便于索引,但不便于插入删除;Vector:基于数组实现,线程安全的,效率低)。
Map接口有三个实现类(HashMap:基于hash表的Map接口实现,非线程安全,高效,支持null值和null键;HashTable:线程安全,低效,不支持null值和null键;LinkedHashMap:是HashMap的一个子类,保存了记录的插入顺序;SortMap接口:TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序)。
Set接口有两个实现类(HashSet:底层是由HashMap实现,不允许集合中有重复的值,使用该方式时需要重写equals()和hashCode()方法;LinkedHashSet:继承与HashSet,同时又基于LinkedHashMap来进行实现,底层使用的是LinkedHashMp)。
区别
List集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get(i)方法来获取集合中的元素;Map中的每一个元素包含一个键和一个值,成对出现,键对象不可以重复,值对象可以重复;Set集合中的对象不按照特定的方式排序,并且没有重复对象,但它的实现类能对集合中的对象按照特定的方式排序,例如TreeSet类,可以按照默认顺序,也可以通过实现Java.util.Comparator<Type>
接口来自定义排序方式。
168、数组和链表的区别
- 数组是将元素在内存中连续存储的;它的优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高;它的缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据量比较大的时候,有可能会出现越界的情况,数据比较小的时候,又有可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低。链表是动态申请内存空间,不需要像数组需要提前申请好内存的大小,链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系)
169、Java中ArrayList和Linkedlist区别?
ArrayList和Vector使用了数组的实现,可以认为ArrayList或者Vector封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向。
LinkedList使用了循环双向链表数据结构。与基于数组的ArrayList相比,这是两种截然不同的实现技术,这也决定了它们将适用于完全不同的工作场景。
LinkedList链表由一系列表项连接而成。一个表项总是包含3个部分:元素内容,前驱表和后驱表,
在JDK的实现中,无论LikedList是否为空,链表内部都有一个header表项,它既表示链表的开始,也表示链表的结尾。表项header的后驱表项便是链表中第一个元素,表项header的前驱表项便是链表中最后一个元素。
170、请用两个队列模拟堆栈结构
两个队列模拟一个堆栈,队列是先进先出,而堆栈是先进后出。模拟如下队列a和b
(1)入栈:a队列为空,b为空。例:则将”a,b,c,d,e”需要入栈的元素先放a中,a进栈为”a,b,c,d,e”
(2)出栈:a队列目前的元素为”a,b,c,d,e”。将a队列依次加入Arraylist集合a中。以倒序的方法,将a中的集合取出,放入b队列中,再将b队列出列。代码如下:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
public class Test9 {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();// a队列
Queue<String> queue2 = new LinkedList<String>(); // b队列
ArrayList<String> a = new ArrayList<String>(); // arrylist集合是中间参数
// 往a队列添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
System.out.print("进栈:");
// a队列依次加入list集合之中
for (String q : queue) {
a.add(q);
System.out.print(q);
}
// 以倒序的方法取出(a队列依次加入list集合)之中的值,加入b对列
for (int i = a.size() - 1; i >= 0; i--) {
queue2.offer(a.get(i));
}
// 打印出栈队列
System.out.println("");
System.out.print("出栈:");
for (String q : queue2) {
System.out.print(q);
}
}
}
171、Collection和Map的集成体系
Collection
类别 | 接口 | 实现类 |
---|---|---|
集合 | Collection | ArrayList, LinkedList, HashSet |
列表 | List | ArrayList, LinkedList |
队列 | Queue | LinkedList, PriorityQueue |
双端队列 | Deque | LinkedList |
栈 | Stack | Stack |
集合视图 | Set | HashSet, TreeSet |
排序集合 | SortedSet | TreeSet |
映射 | Map | HashMap, TreeMap |
排序映射 | SortedMap | TreeMap |
集合工具类 | Collections | - |
接口/类 | 继承关系 |
---|---|
Collection | |
-List | -ArrayList |
-LinkedList | |
-Vector | |
-Stack | |
-Set | -HashSet |
-LinkedHashSet | |
-TreeSet | |
-Queue | -Deque |
-PriorityQueue | |
-Map | -HashMap |
-LinkedHashMap | |
-TreeMap | |
-Hashtable | |
-Properties |
下面是 Collection 接口的集成体系和继承关系的详细介绍:
-
Collection 接口是 Java 集合框架中的根接口,它定义了一组通用的方法,用于操作集合中的元素。它是 List、Set 和 Queue 接口的父接口。
-
List 接口继承自 Collection 接口,它表示一个有序的集合,允许存储重复的元素。List 接口的常用实现类有 ArrayList 和 LinkedList。
-
Set 接口也继承自 Collection 接口,它表示一个不允许存储重复元素的集合。Set 接口的常用实现类有 HashSet 和 TreeSet。
-
Queue 接口也继承自 Collection 接口,它表示一个先进先出(FIFO)的队列。Queue 接口的常用实现类有 LinkedList 和 PriorityQueue。
-
List 接口的子接口有两个,分别是 RandomAccess 和 Cloneable。RandomAccess 接口标识实现类支持高效的随机访问,而 Cloneable 接口标识实现类支持克隆操作。
-
Set 接口的子接口有两个,分别是 SortedSet 和 NavigableSet。SortedSet 接口表示一个有序的集合,NavigableSet 接口在 SortedSet 的基础上增加了一些导航方法。
-
Queue 接口的子接口有两个,分别是 Deque 和 BlockingQueue。Deque 接口表示一个双端队列,它支持在两端进行插入和删除操作。BlockingQueue 接口在 Deque 的基础上增加了一些阻塞方法。
下面是一个表格,列出了 Collection 接口及其子接口的继承关系:
接口 | 继承关系 |
---|---|
Collection | |
List | Collection |
Set | Collection |
Queue | Collection |
RandomAccess | List |
Cloneable | List |
SortedSet | Set |
NavigableSet | SortedSet |
Deque | Queue |
BlockingQueue | Queue |
这些接口和实现类组成了 Java 集合框架的基础,提供了丰富的数据结构和算法,方便开发人员进行集合操作。
Map
Map | 继承关系 | 详细介绍说明 |
---|---|---|
HashMap | extends AbstractMap | 基于哈希表实现的 Map 接口,线程不安全,高效,无序 |
LinkedHashMap | extends AbstractMap | 基于哈希表实现的 Map 接口,线程不安全,高效,有序 |
TreeMap | extends AbstractMap | 基于红黑树实现的 Map 接口,线程不安全,高效,有序 |
EnumMap | extends AbstractMap | 基于 Enum储蓄的 Map 接口,线程不安全,高效,有序 |
ConcurrentHashMap | extends AbstractMap | 基于哈希表实现的线程安全的 Map 接口,高效,无序 |
CopyOnWriteMap | extends AbstractMap | 基于哈希表实现的线程安全的 Map 接口,高效,有序 |
下面是Map集成体系及继承关系的详细介绍表格:
类别 | 类名 | 描述 |
---|---|---|
接口 | Map | 定义了基本的Map操作方法,如插入、删除、查找等 |
抽象类 | AbstractMap | 实现了Map接口的一部分方法,提供了一些默认的实现 |
实现类 | HashMap | 基于哈希表的Map实现,支持快速的插入、删除和查找操作 |
实现类 | LinkedHashMap | 基于哈希表和双向链表的Map实现,保持了插入顺序或访问顺序 |
实现类 | TreeMap | 基于红黑树的Map实现,按照键的自然顺序或自定义顺序进行排序 |
实现类 | ConcurrentHashMap | 基于分段锁的并发Map实现,支持高并发环境下的线程安全操作 |
实现类 | Hashtable | 基于哈希表的线程安全Map实现,支持同步操作 |
实现类 | WeakHashMap | 基于弱引用的Map实现,在没有其他引用时可以被垃圾回收器回收 |
实现类 | IdentityHashMap | 基于引用相等而不是值相等的Map实现,使用==进行比较 |
实现类 | EnumMap | 用于枚举类型作为键的Map实现,内部使用数组实现 |
实现类 | Properties | 特殊的Map实现,用于处理属性文件,键和值都是字符串类型 |
以上是常见的Map集成体系及继承关系,每个类都有其特定的用途和适用场景。你可以根据具体需求选择合适的Map实现类来使用。
172、什么是线程池,如何使用?
线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。
在JDK的java.util.concurrent.Executors中提供了生成多种线程池的静态方法。
-
ExecutorService newCachedThreadPool=Executors.newCachedThreadPool();
-
ExecutorService newFixedThreadPool=Executors.newFixedThreadPool(4);
-
ScheduledExecutorService newScheduledThreadPool=Executors.newScheduledThreadPool(4);
-
ExecutorService newSingleThreadExecutor=Executors.newSingleThreadExecutor();
然后调用他们的execute方法即可。
173、常用的线程池有哪些?
-
new SingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
-
new FixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
-
new CachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
-
new ScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
174、请叙述一下您对线程池的理解?
(如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)合理利用线程池能够带来三个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
175、说说你对Java中反射的理解
Java中的反射首先是能够获取到Java中要反射类的字节码,获取字节码有三种方法:
-
Class.forName(className)
-
类名.class
-
this.getClass()。
然后将字节码中的方法,变量,构造函数等映射成相应的Method、Filed、Constructor等类,这些类提供了丰富的方法可以被我们所使用。
176、动静态代理的区别,什么场景使用?
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现JDK里的InvocationHandler
接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的new ProxyInstance
得到代理对象。
还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
AOP编程就是基于动态代理实现的,比如著名的Spring框架、Hibernate框架等等都是动态代理的使用例子。
177、写一个ArrayList的动态代理类
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.Proxy;
public class Test9 {
public static void main(String[] args) {
final List<String> list = new ArrayList<String>();
List<String> proxyInstance = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(),
list.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(list, args);
}
});
proxyInstance.add("你好");
System.out.println(list);
}
}
178、你所知道的设计模式有哪些
Java中一般认为有23种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
179、你对软件开发中迭代的含义的理解?
- 答:软件开发中,各个开发阶段不是顺序执行的,应该是并行执行,也就是迭代的意思。这样对于开发中的需求变化,及人员变动都能得到更好的适应。
二、JavaWeb部分
1、四种会话跟踪技术
-
page否是代表与一个页面相关的对象和属性。一个页面由一个编译好的Javaservlet类(可以带有任何的include指令,但是没有include动作)表示。这既包括servlet又包括被编译成servlet的JSP页面)
-
request是是代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件(由于forward指令和include动作的关系)
-
session是是代表与用于某个Web客户机的一个用户体验相关的对象和属性。一个Web会话可以也经常会跨越多个客户机请求
-
application是是代表与整个Web应用程序相关的对象和属性。这实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域
2、Jsp九大隐式对象
- request:用户端请求,此请求会包含来自GET/POST请求的参数
- response:网页传回用户端的回应
- pageContext:网页的属性是在这里管理
- session:与请求有关的会话期
- application:servlet正在执行的内容
- out:用来传送回应的输出
- config:servlet的构架部件
- page:JSP网页本身
- exception:针对错误网页,未捕捉的例外
- 这些隐式对象可以在JSP页面中直接使用,无需进行额外的声明或初始化。它们提供了方便的访问和操作方式,使得开发人员可以更轻松地处理和操作Web应用程序中的数据和信息。
3、Servlet的生命周期
Servlet的生命周期是指Servlet对象从创建到销毁的整个过程。在Web应用程序中,Servlet容器负责管理Servlet的生命周期。下面是Servlet的生命周期详解:
-
加载和实例化:当Servlet容器启动时,会根据web.xml配置文件或注解扫描机制加载并实例化Servlet对象。这个过程只会发生一次。
-
初始化:在实例化后,Servlet容器会调用Servlet的init()方法来进行初始化。在这个方法中,可以进行一些初始化操作,例如加载配置文件、建立数据库连接等。
-
服务:初始化完成后,Servlet容器会调用Servlet的service()方法来处理客户端的请求。每次请求都会创建一个新的线程来处理,多个线程可以同时调用同一个Servlet的service()方法。
-
销毁:当Servlet容器关闭或Web应用程序被卸载时,会调用Servlet的destroy()方法来销毁Servlet对象。在这个方法中,可以进行一些资源释放的操作,例如关闭数据库连接、释放内存等。
需要注意的是,Servlet的service()方法会根据请求的类型(GET、POST等)调用相应的doGet()、doPost()等方法来处理具体的业务逻辑。这些方法可以被重写,以实现自定义的处理方式。
另外,Servlet还有一些其他的方法,如getServletConfig()、getServletContext()等,用于获取Servlet的配置信息和上下文信息。
总结起来,Servlet的生命周期包括加载和实例化、初始化、服务和销毁四个阶段。通过重写相应的方法,可以实现自定义的初始化和销毁操作,以及处理客户端请求的业务逻辑。
4、Servlet和Jsp的关系
JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。
JSP编译后是"类servlet"。
Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。
而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
JSP侧重于视图,Servlet主要用于控制逻辑。
Servlet和JSP(JavaServer Pages)是Java Web开发中的两个重要组件,它们之间有密切的关系。
Servlet是Java编写的服务器端组件,用于处理客户端的请求并生成响应。它可以接收HTTP请求并根据请求的内容执行相应的逻辑操作,然后生成响应返回给客户端。Servlet通常被用于处理业务逻辑、数据库操作、文件上传等任务。
JSP是一种基于HTML的模板技术,它允许在HTML页面中嵌入Java代码。JSP页面在服务器端被解析和编译成Servlet,并由Servlet容器进行管理和执行。JSP页面可以使用Java代码和JSP标签库来动态生成HTML内容,使得页面能够根据不同的请求动态生成不同的内容。
Servlet和JSP之间的关系是相辅相成的。通常情况下,Servlet用于处理业务逻辑和控制流程,而JSP用于生成页面的显示内容。Servlet可以通过请求转发或者重定向的方式将控制权交给JSP来生成页面。在JSP中,可以使用内置对象(如request、response、session等)来访问和操作Servlet中的数据。
总结起来,Servlet负责业务逻辑的处理,而JSP负责页面的展示。它们共同构成了Java Web应用程序的核心组件,可以协同工作来实现动态的Web应用程序。
5、什么是MVC?
- MVC是Model-View-Controller的简写。
- Model代表的是应用的业务逻辑(通过JavaBean,EJB组件实现)。
- View是应用的表示页面(由JSP页面产生)。
- Controller是提供应用的处理过程控制(一般是一个Servlet)。
- 通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。
- 这些组件可以进行交互和重用。
6、重定向(redirect)和转发(forward)的异同点
- 相同点:
- 实现请求之间的跳转
- 不同:
- 语法不同
- 重定向不能携带值,转发可以带值
- 重定向发送了两次请求,转发发送了一次请求(服务器内部跳转)
- 重定向地址发生改变,转发地址不发生改变
request.setAttribute("mesage", "注册成功"); // ${message}
request.getRequestDispatcher("index.jsp")
.forward(request, response); // 转发
response.sendRedirect("index.jsp"); // 重定向
7、Session和Cookie的区别
Session | Cookie |
---|---|
在服务器端保存用户信息 | 在客户端保存用户信息 |
session中保存的是 Object类型 | cookie保存的是 String类型 |
随会话的结束而将其存储 的数据销毁 | cookie可以长期保存在 客户端 |
保存重要的信息 | 保存不重要的用户信息 |
- Session和Cookies都能设置失效时间
8、get和post的区别
- 从表面现像上面看GET和POST的区别:
- GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?name=zhagnsan&password=123456。POST把提交的数据则放置在是HTTP包的包体中。
- GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据。其实这样说是错误的,不准确的:
“GET方式提交的数据最多只能是1024字节",因为GET是通过URL提交数据,那么GET可提交的数据量就跟
URL的长度有直接关系了。而实际上,URL不存在参数上限的问题,HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、
FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。 - POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-siterequestforgery攻击。
Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求,在FORM(表单)中,Method
默认为"GET",实质上,GET和POST只是发送机制不同,并不是一个取一个发!
9、写出浏览器JS的对象
- Window对象:窗口信息
- History对象:包含了浏览器窗口访问过的URL。
- Location对象:Location包含了当前URL的信息
- Navigator对象:包含有关浏览器的信息
- Screen对象:包含有关客户端显示屏幕的信息
- Document对象:文档对象
10、简述在JSP中的注释有哪几种方法
主要有四种方法:
<%--
与--%>
//
/**
与**/
<!--
与-->
11、JSP中动态INCLUDE与静态INCLUDE的区别?
答:
动态INCLUDE用jsp:include动作实现
<jsp:include page="included.jsp" flush="true"/>
它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数静态INCLUDE用include伪码实现,定不会检查所含文件的变化,
适用于包含静态页面<%@includefile=included.htm%>
12、尽量多的写出request下的常用方法
getCookies()
:从请求信息中获取Cookies
getHeader(String name)
:获取请求头信息
getRequestURI()
:获取请求的URI地址
getSession()
:获取Session对象
getParameter(String name)
:获取请求参数
setCharacterEncoding(String env)
:设置请求编码
…
13、BS与CS的联系与区别。
- C/S是Client/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、InFORMix或SQLServer。客户端需要安装专用的客户端软件。
- B/S是Brower/Server的缩写,客户机上只要安装一个浏览器(Browser),如NetscapeNavigator或InternetExplorer,服务器安装Oracle、Sybase、InFORMix或SQLServer等数据库。在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现。浏览器通过WebServer同数据库进行数据交互。
- C/S与B/S区别:
- 1.硬件环境不同:
C/S一般建立在专用的网络上,小范围里的网络环境,局域网之间再通过专门服务器提供连接和数据交换服务.
B/S建立在广域网之上的,不必是专门的网络硬件环境,例与电话上网,租用设备.信息自己管理.有比C/S更强的适应范围,一般只要有操作系统和浏览器就行 - 2.对安全要求不同
C/S一般面向相对固定的用户群,对信息安全的控制能力很强.一般高度机密的信息系统采用C/S结构适宜.可以通过B/S发布部分可公开信息.
B/S建立在广域网之上,对安全的控制能力相对弱,可能面向不可知的用户。 - 3.对程序架构不同
C/S程序可以更加注重流程,可以对权限多层次校验,对系统运行速度可以较少考虑.
B/S对安全以及访问速度的多重的考虑,建立在需要更加优化的基础之上.比C/S有更高的要求B/S结构的程序架构是发展的趋势,从MS的.Net系列的BizTalk2000Exchange2000等,全面支持网络的构件搭建的系统.SUN和IBM推的JavaBean构件技术等,使B/S更加成熟.
- 1.硬件环境不同:
14、Ajax的工作原理?
在会话的开始,浏览器加载Ajax引擎。
请求动作通过JavaScript调用Ajax引擎来代替。
引擎负责绘制用户界面以及与服务器端通讯。
Ajax引擎采用异步交互过程--不用等待服务器的通讯。
15、XMLHttpRequest对象是什么,它有哪些常用方法?
一个JavaScript对象。是Ajax的核心。
该对象的方法和属性。
open():建立到服务器的新请求。
send():向服务器发送请求。
abort():退出当前请求。
readyState:提供当前HTML的就绪状态。
responseText:服务器返回的请求响应文本。
16、HTTP常见的状态码
状态码 | 说明 |
---|---|
200 | 服务器相应正常 |
400 | 错误请求,如语法错误 |
403 | 没有访问权限 |
404 | 访问的资源不存在 |
500 | 服务器内部错误 |
17、数据库规范:三大范式
- 第一范式的目标是确保每列都是不可再分的最小数据单元。
- 第二范式每要求一个表只描述一件事情。
- 第三范式要求表中各列必须和主键直接相关,不能间接相关。
18、实体之间的关系
- 一对一:学生和学生证
- 一对多:班级和学生
- 多对一:学生和班级
- 多对多:商品和订单
19、完整性约束
- 实体完整性
- 域完整性
- 引用完整性
- 自定义完整性
20、常用的端口
- TomCat:8080
- 浏览器Http:80
- MySql:3306
- Sqlerver:1433
- Oracle:1521
21、Servlet和Jsp的关系
- JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。
- JSP编译后是"类servlet"。
- Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。
- 而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
- JSP侧重于视图,Servlet主要用于控制逻辑。
22、Oracle中基本数据类型
Oracle的基本数据类型(常用):
- 字符型
Char固定长度字符串占2000个字节
Varchar2可变长度字符串占4000个字节
Nvarchar2占2000个字符(最多能存2000个字母/中文) - 大对象型(lob)
Blob:二进制数据最大长度4GBlob用于存一些图片,视频,文件。
Clob:字符数据最大长度4G,可以存大字符串 - 数值型
Integer整数类型,小的整数。
Float浮点数类型。
Number(p,s)包含小数位的数值类型。P表示精度,s表示小数后的位数。
Eg:number(10,2)表示小数点之前可有8位数字,小数点后有2位。 - 日期类型
Date日期(日-月-年)DD-MM-YY(HH-MI-SS)
Timestamp跟date比它可以精确到微秒。精确范围0~9默认为6。
23、JDBC访问数据库步骤
- JDBC API主要功能:与数据库建立连接、执行SQL语句、处理结果
- DriverManager :依据数据库的不同,管理JDBC驱动
- Connection :负责连接数据库并担任传送数据的任务
- Statement :由Connection产生、负责执行SQL语句
- ResultSet :负责保存Statement执行后产生的查询结果
try{
// 1、加载JDBC驱动
Class.forName(JDBC驱动类);
}
... ...
try{
// 2、与数据库建立连接(URL用来表示数据库)
Connection con = DriverManager.getConnection(URL, 数据库用户名, 密码);
// 3、发送SQL语句,并得到返回结果
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
// 4、处理返回结果
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}
// 5、释放资源
rs.close();
stmt.close();
con.close();
}
各数据库驱动类:
// SqlServer驱动类:
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
"jdbc:sqlserver://localhost:1433;databaseName=数据库名","用户名","密码"
// MySql驱动类:
Class.forName("com.mysql.jdbc.Driver");
"jdbc:mysql://localhost:3306/数据库名","用户名","密码"
// Oracle驱动类:
Class.forName("oracle.jdbc.OracleDriver");
"jdbc:oracle:thin:@localhost:1521:数据库名","用户名","密码"
24、Ajax总结
AJAX全称:异步无刷新访问技术
Ajax的核心是JavaScript对象XmlHttpRequest(XHR)。
Ajax的优点:
1.提高用户体验度(UE)
2.提高应用程序的性能
3.进行局部刷新
通常会使用Jquery进行封装,使用其他起来更方便。
示例:
$.ajax({
"url": "地址",
"data": "要发送的数据",
"dataType": "json/text",
"type": "get/post",
"success":function(){
//正确执行
},
"error":function(){
//失败执行
}
}
25、Json(JavaScript Object Notation)描述
A、一种轻量级的数据交换格式
B、通常用于在客户端和服务器之间传递数据
优点:
轻量级交互语言
结构简单
易于解析
26、如何解决“Port 8080 is already in use”的问题
在开发工程中常常会遇到“Port 8080 is already in use”异常,然后webserver不能启动,这是因为已经有其他程序或者进程占用了相应的端口,解决这个问题就要找出到底是哪个程序占用了这个端口,然后关掉那个程序或者杀掉那个进程就可以了。
解决的步骤如下:
1.打开一个命令窗口,执行netstat -a -o命令。
2.找到占用那个端口的程序,找到它的pid。
3.打开任务管理器,根据pid找到它的进程,杀掉这个进程。
4.重新启动或者运行你的服务或者程序你就会发现这个异常没有了。
27、innerText和innerHtml的区别
innerText
:获取或者设置一个标签中的内容,该内容只能是文本。innerHtml
:获取或者设置一个标签中的内容,该内容可以是文本和标签。text()
和html()
是Jquery中方法,效果一样的。
28、$(document).ready()
是个什么函数?为什么要用它?
ready()
函数用于在文档进入ready状态时执行代码。当DOM完全加载(例如HTML被完全解析DOM树构建完成时),jQuery允许你执行代码。使用$(document).ready()
的最大好处在于它适用于所有浏览器,jQuery帮你解决了跨浏览器的难题。
29、JavaScript的window.onload事件和jQuery的ready函数有何不同
Session | Cookie | |
---|---|---|
执行时机 | 必须等待网页中所有的内容加载 完毕后(包括图片、flash、视频 等)才能执行 | 网页中所有DOM文档结构绘制完毕 后即刻执行,可能与DOM元素关联 的内容(图片、flash、视频等)并 没有加载完 |
编写个数 | 同一个页面不能同时编写多个 | 同一个页面不能同时编写多个 |
简化写法 | 无 | $(function()){ //执行代码 }); |
30、Jquery中有哪几种常用类型的选择器?
jQuery 提供了多种选择器,用于快速、方便地选取和操作 DOM 元素。以下是 jQuery 中常用的几种选择器:
基本选择器:直接根据id、css类名、元素名返回匹配的dom元素。
层次选择器:也叫做路径选择器,可以根据路径层次来选择相应的DOM元素。
过滤选择器:在前面的基础上过滤相关条件,得到匹配的dom元素。
属性选择器:选取具有指定属性的元素。
详细如下:
1. 基本选择器
-
元素选择器:选取所有指定标签名的元素。
$('p'); // 选取所有 <p> 元素
-
ID 选择器:选取具有指定 ID 的元素(ID 应唯一)。
$('#myId'); // 选取 ID 为 'myId' 的元素
-
类选择器:选取具有指定类名的所有元素。
$('.myClass'); // 选取所有类名为 'myClass' 的元素
2. 组合选择器
-
群组选择器:选取多个不同选择器的元素。
$('p, .myClass, #myId'); // 选取所有 <p> 元素、类名为 'myClass' 的元素和 ID 为 'myId' 的元素
-
后代选择器:选取某元素内的所有指定元素。
$('div p'); // 选取所有位于 <div> 元素内的 <p> 元素
-
子选择器:选取某元素内的直接子元素。
$('div > p'); // 选取所有直接位于 <div> 元素内的 <p> 元素
3. 属性选择器
-
基本属性选择器:选取具有指定属性的元素。
$('input[name="username"]'); // 选取所有 name 属性为 'username' 的 <input> 元素
-
属性值选择器:选取具有指定属性和值的元素。
$('input[name="username"]'); // 选取所有 name 属性为 'username' 的 <input> 元素 $('input[name^="user"]'); // 选取所有 name 属性以 'user' 开头的 <input> 元素 $('input[name$="name"]'); // 选取所有 name 属性以 'name' 结尾的 <input> 元素 $('input[name*="ser"]'); // 选取所有 name 属性包含 'ser' 的 <input> 元素
4. 表单选择器
-
:input:选取所有
<input>
,<textarea>
,<select>
, 和<button>
元素。$(':input');
-
:text:选取所有
<input type="text">
元素。$(':text');
-
:password:选取所有
<input type="password">
元素。$(':password');
-
:submit:选取所有
<input type="submit">
元素。$(':submit');
-
:button:选取所有
<button>
元素和<input type="button">
元素。$(':button');
5. 内容选择器
-
:contains(text):选取包含指定文本的元素。
$('div:contains("Hello")'); // 选取所有包含文本 "Hello" 的 <div> 元素
-
:empty:选取没有子元素(包括文本节点)的空元素。
$('div:empty'); // 选取所有没有子元素的 <div> 元素
6. 可见性选择器
-
:visible:选取所有可见的元素。
$('div:visible'); // 选取所有可见的 <div> 元素
-
:hidden:选取所有不可见的元素。
$('div:hidden'); // 选取所有不可见的 <div> 元素
7. 位置选择器
-
:first:选取第一个元素。
$('p:first'); // 选取第一个 <p> 元素
-
:last:选取最后一个元素。
$('p:last'); // 选取最后一个 <p> 元素
-
:eq(index):选取索引等于指定值的元素(索引从 0 开始)。
$('li:eq(2)');
31、Jquery中如何实现节点遍历?
each()
是 jQuery 中一个非常重要且常用的方法,用于遍历 jQuery 对象中的每一个元素。它提供了一种简洁而强大的方式来对集合中的每个元素执行相同的操作。
语法
$.each(array, function(index, value));
$.each(object, function(key, value));
参数
- array:要遍历的数组。
- object:要遍历的对象。
- function(index, value):为每个元素执行的回调函数。
- index:当前元素的索引(对于数组)或键名(对于对象)。
- value:当前元素的值。
返回值
each()
方法返回原始的 jQuery 对象,这使得它支持链式调用。
示例
遍历数组
var fruits = ['apple', 'banana', 'cherry'];
$.each(fruits, function(index, value) {
console.log(index + ': ' + value);
});
// 输出:
// 0: apple
// 1: banana
// 2: cherry
遍历对象
var person = {
name: 'John',
age: 30,
city: 'New York'
};
$.each(person, function(key, value) {
console.log(key + ': ' + value);
});
// 输出:
// name: John
// age: 30
// city: New York
遍历 jQuery 对象
$('div').each(function(index, element) {
console.log(index + ': ' + $(element).text());
});
// 假设页面上有三个 <div> 元素,内容分别为 "Div 1", "Div 2", "Div 3"
// 输出:
// 0: Div 1
// 1: Div 2
// 2: Div 3
使用 each()
进行 DOM 操作
each()
方法常用于对 DOM 元素进行批量操作,例如修改样式、添加事件监听器等。
$('p').each(function(index, element) {
$(element).css('color', 'red');
});
// 将所有 <p> 元素的文本颜色改为红色
终止遍历
在回调函数中,可以通过返回 false
来终止遍历。
$('p').each(function(index, element) {
if (index === 2) {
return false; // 当索引为 2 时,终止遍历
}
$(element).css('color', 'blue');
});
// 将所有 <p> 元素(除了第三个)的文本颜色改为蓝色
注意事项
each()
方法不会改变原始的 jQuery 对象。- 回调函数中的
this
关键字指向当前遍历的 DOM 元素,可以直接使用$(this)
来操作当前元素。
$('div').each(function(index, element) {
$(this).css('border', '1px solid black'); // 使用 $(this) 操作当前元素
});
总结
each()
方法是 jQuery 中一个非常灵活和强大的工具,适用于各种遍历和操作集合元素的需求。无论是数组、对象还是 jQuery 对象,each()
都能轻松应对,并且支持链式调用和终止遍历,使得代码更加简洁和高效。
32、Jquery中实现Ajax语法?
AJAX全称:异步无刷新访问技术
Ajax的核心是JavaScript对象XmlHttpRequest(XHR)。
Ajax的优点:
1.提高用户体验度(UE)
2.提高应用程序的性能
3.进行局部刷新
通常会使用Jquery进行封装,使用其他起来更方便。
$.ajax({
"url": "地址",
"data": "要发送的数据",
"dataType": "json/text",
"type": "get/post",
"success":function(){
//正确执行
},
"error":function(){
//失败执行
}
}
33、Js和Jquery的关系
- JS:是JavaScript的简称
- Jquery是一个轻量级的JS框架,使用起来灵活,兼容强,有很丰富的插件。
- 例如:JqueryUI,开发起来更加的方便。
34、常见的应用服务器有那些?
BEA WebLogicServer,IBM WebSphere,jBoss,Tomcat等
35、MODEL1和MODEL2
35.1、Model1
Model1是最早的能体现分层思想的一种开发模式,简单来说Model1就是一种Jsp+JavaBean的一种开发模式。
缺点:可维护性和可扩展性是比较差的。
35.2、Model2
思路:
①首先由Jsp页面向Servlet提交一个请求。
②然后在我们的控制层Servlet实例化一个模型层的对象或者调用模型层的一些功能。
③然后由模型层来访问读取我们的数据库层。
④当得到读取到结果后,再将结果返回到我们的控制层。
⑤控制层得到这个结果后,会根据这个结果给用户展示不同的Jsp页面。
36、在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?
public static String translate(String str) {
String tempStr = "";
try {
tempStr = new String(str.getBytes("ISO-8859-1"), "GBK");
tempStr = tempStr.trim();
} catch (Exception e) {
System.err.println(e.getMessage());
}
return tempStr;
}
37、http的长连接和短连接
HTTP协议有HTTP/1.0版本和HTTP/1.1版本。HTTP1.1默认保持长连接(HTTPpersistentconnection,也翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据;相反的就是短连接。
在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。从HTTP/1.1起,默认使用的是长连接,用以保持连接特性。
38、http常见的状态码有哪些?
200 OK //客户端请求成功
301 Moved Permanently(永久移除),请求的URL已移走。Response中应该包含一个LocationURL,说明资源现在所处的位置
302 found重定向
400 Bad Request//客户端请求有语法错误,不能被服务器所理解
401 Unauthorized//请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden//服务器收到请求,但是拒绝提供服务
404 Not Found//请求资源不存在,eg:输入了错误的URL
500 Internal Server Error//服务器发生不可预期的错误
503 Server Unavailable//服务器当前不能处理客户端的请求,一段时间后可能恢复正常
三、框架部分
1、IOC的概念
控制反转(InversionofControl,英文缩写为IOC)是一个重要的面向对象编程的法则来消减计算机程序的偶和问题,也是轻量级的Spring框架的核心。控制反转一般分为两种类型,依赖注入和依赖查找。依赖注入应用比较广泛
2、注入的三种方式
(1)接口注入
(2)set注入
调用对象的set方法给属性赋值,调用的是无参数的构造方法创建对象。
(3)构造注入
Spring容器调用指定的构造方法,给对象属性赋值。
3、MyBatis的缓存机制
提供了一级缓存和二级缓存
一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Sessionflush或close之后,该Session中的所有Cache就将清空。二级缓存与一级缓存及至相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可定义第三方存储源,如Ehcache框架等。
对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespace)进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear。
MyBatis中一级缓存是默认开启的,即在查询中(一次SqlSession中)。只要当SqlSession不关闭,那么你的操作会默认存储使用一级缓存。
4、解释Spring支持的几种bean的作用域
Spring框架支持以下五种bean的作用域:
singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
缺省的Spring bean 的作用域是Singleton
5、在 Spring中如何注入一个java集合
Spring提供以下几种集合的配置元素:
<list>
类型用于注入一列值,允许有相同的值。
<set>
类型用于注入一组值,不允许有相同的值。
<map>
类型用于注入一组键值对,键和值都可以为任意类型。
<props>
类型用于注入一组键值对,键和值都只能为String类型。
6、动态代理设计模式
动态代理类是一个在运行时由开发人员所指定的一列接口的实现。动态代理接口是一种由代理类实现的接口,并且是一个java.lang.reflect.Proxy类的实例。每一个代理实例都与一个调用处理器对象相联,这个调用处理器实现了java.lang.reflect.InvocationHandler接口。在代理实例上的一个方法调用是通过其中之一的代理接口被转发到与这个代理实例相联的调用处理的invoke方法上。一个java.lang.reflect.Method对象会决定那一个方法会被调用,一个类型为java.lang.Object的数组包含调用的参数。调用处理器会适当地解码方法的调用(encodedmethodinvocationasappropriate),并且它(调用处理器)的返回结果被作为在代理实例上方法调用返回的结果而返回。
7、Spring中通知的类型
通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。
Spring切面可以应用五种类型的通知:
before:前置通知,在一个方法执行前被调用。
after: 在方法执行之后调用的通知,无论方法执行是否成功。
after-returning: 仅当方法成功完成后执行的通知。
after-throwing: 在方法抛出异常退出时执行的通知。
around: 在方法执行之前和之后调用的通知。
8、Hibernate框架工作原理
Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,使得java程序员可以随心所欲的使用对象编程思维来操纵数据库。
工作原理:
1.读取并解析配置文件
2.读取并解析映射信息,创建SessionFactory
3.打开Sesssion
4.创建事务Transation
5.持久化操作
6.提交事务
7.关闭Session
8.关闭SesstionFactory
Hibernate的优点有:
1.对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
2.Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
3、Hibernate使用Java反射机制而不是字节码增强程序来实现透明性。
4、Hibernate的性能好,映射的灵活性比较出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
hibernate的核心类有:
Configuration、SessionFactory、Session。
9、Hibernate对象的三种状态是什么
瞬时态:一个Java对象创建之后,还没新增到数据库之前的状态
特点:
(1)不和Session实例关联
(2)在数据库中没有和瞬时对象关联的记录
持久态:当调用save()或者是saveOrUpdate()方法之后的状态,保存到数据库的状态
特点:
(1)和Session实例关联
(2)在数据库中有和持久对象关联的记录
游离态:当调用session的Close方法或者清空session之后对象的状态
特点:
(1)本质上和瞬时对象相同
(2)只是比瞬时对象多了一个数据库记录标识值id.
10、Hibernate对象的三种状态如何转换的
1.瞬时对象转为持久对象:
(1)通过Session的save()和saveOrUpdate()方法把一个瞬时对象与数据库相关联,这个瞬时对象就成为持久化对象。
(2)使用fine(),get(),load()和iterater()方法查询到的数据对象,将成为持久化对象。
2.持久对象转为脱管对象:
(1)当执行close()或clear(),evict()之后,持久对象会变为脱管对象。
3.脱管对象转为持久对象:
(1)通过Session的update(),saveOrUpdate()和lock()等方法,把脱管对象变为持久对象。
11、Hibernate分页怎样实现?
public void fenye() {
int pageIndex = 1;// 7g
int pageSize = 5;// m★/
int pageCount = 0;// s7%t
int totalcount = 0;// %5#
Session session = HibernateUtil.getSession();
Query query = session.createQuery("select count(*) from Emp"); // 总条数
totalcount = Integer.parseInt(query.uniqueResult() + "");// 总条数
pageCount = (totalcount % pageSize == 0) ? (pageCount = totalcount / pageSize)
: (pageCount = totalcount / pageSize + 1);
Query query2 = session.createQuery("from Emp");// 查询所有数据
query2.setFirstResult((pageIndex - 1) * pageSize);// 设置开始查询的位置
query2.setMaxResults(pageSize);// 设置页面的大小
List<Emp> list = query2.list();
System.out.println("当前页码:" + pageIndex + "\n" + "当前大小:" + pageSize + "\n" + "总页数:" + pageCount);
for (Emp emp : list) {
System.out.println(emp);
}
}
12、对象关系映射(ObjectRelationalMapping,简称ORM)
ORM是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。
WhyORM?
面向对象的开发方法是当今企业级应用开发环境中的主流开发方法。常见的ORM框架有:Hibernate,MyBatis等
13、hibernate拒绝连接、服务器崩溃的原因?最少写5个
1.db没有打开
2.网络连接可能出了问题
3.连接配置错了
4.驱动的driver,url是否都写对了
5.LIB下加入相应驱动,数据连接代码是否有误
6.数据库配置可能有问题
7.当前联接太多了,服务器都有访问人数限制的
8.服务器的相应端口没有开,即它不提供相应的服务
14、MyBatis与Hibernate有什么不同?
相同点:屏蔽jdbcapi的底层访问细节,使用我们不用与jdbcapi打交道,就可以访问数据。
jdbcapi编程流程固定,还将sql语句与java代码混杂在了一起,经常需要拼凑sql语句,细节很繁琐。
Mybatis的好处:屏蔽jdbcapi的底层访问细节;将sql语句与java代码进行分离;提供了将结果集自动封装称为实体对象和对象的集合的功能,queryForList返回对象集合,用queryForObject返回单个对象;提供了自动将实体对象的属性传递给sql语句的参数。
Hibernate是一个全自动的orm映射工具,它可以自动生成sql语句,Mybatis需要我们自己在xml配置文件中写sql语句,hibernate要比Mybatis功能负责和强大很多。因为hibernate自动生成sql语句,我们无法控制该语句,我们就无法去写特定的高效率的sql。对于一些不太复杂的sql查询,hibernate可以很好帮我们完成,但是,对于特别复杂的查询,hibernate就很难适应了,这时候用Mybatis就是不错的选择,因为ibatis还是由我们自己写sql语句。
15、Struts2中result中的type类型
1.dispatcher:默认的,用来转发到页面,通常处理JSP
2.redirect:将用户重定向到一个已配置好的URL
3.redirectAction:将用户重定向到一个已定义好的action
4.chain:将action和另外一个action链接起来,转发到Action
5.tream:向浏览器发送InputSream对象对下载的内容和图片非常有用
6.json:Ajax返回Json格式数据(extendsjson-default)
16、Struts2中访问ServletAPI三种方式:
16.1、ServletAPl 解耦的访问方式(推荐)
Map<String,Object> session = null;
session = ActionContext.getContext().getSession();
与Servlet API解耦的访问方式
1、对Servlet API进行封装
提供了三个Map对象访问request、session、application作用域
2、通过ActionContext类获取这三个Map对象
Object get("request")
Map getSession()
Map getApplication()
16.2、与ServletAPI耦合的访问方式(不推荐)
HttpSession session = null;
session = ServletActionContext.getRequest().getSession();
- 通过ServletActionContext类似获取Servlet API对象
- ServletContext getServletContext()
- HttpServletResponse getResponse()
- HttpServletRequest getRequest()
- 通过request.getSession()获取session对象
- 通过xxx.setAttribute()和xxx.getAttribute()功能,
在不同的页面或Action中传递数据
16.3、实现特定接口的方式,向框架注入ServletAPI
实现RequestAware, SessionAware, ApplicationAware接口,代码如下:
public class BaseAction implements RequestAware, SessionAware, ApplicationAware {
public Map<String, Object> request;
public Map<String, Object> session;
public Map<String, Object> application;
@Override
public void setRequest(Map<String, Object> request) {
this.request = request;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
@Override
public void setApplication(Map<String, Object> application) {
this.application = application;
}
}
17、struts2框架的核心控制器是什么?它有什么作用?
- Struts2框架的核心控制器是StrutsPrepareAndExecuteFilter。
- 作用:负责拦截由
<url-pattern>/*</url-pattern>
指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求。默认情况下,如果用户请求的路径不带后缀或者后缀以.action
结尾,这时请求将被转入struts2框架处理,否则struts2框架将略过该请求的处理。
- 作用:负责拦截由
18、Hibernate中get和load之间的区别(重点):
相同点:
都可以根据ID查询一个对象
不同点:
加载方式不同:
- 当lazy=true时,代表是懒加载,get和load之间就有区别
get不管查询出来的对象是否被使用,都会立即发送SQL语句
load如果查询出来的对象没有被使用,就不会立即发送SQL语句,等需要用该对象的时候,才会发送SQL - 当lazy=false时,代表含义是立即加载,get和load都会立即发送SQL
返回结果不同:
- load检索不到记录时,会抛ObjectNotFoundException异常
19、Hibernate缓存概述
一级缓存(session级别缓存),也叫事务级别的缓存
- 一级缓存的生命周期和session的生命周期保持一致,hibernate默认就启用了一级缓存,不能将其关闭,可以通过session.clear()和session.evict(object)来管理一级缓存。其中get,load,iterate都会使用一级缓存,一级缓存缓存的是对象。一级缓存只查询主键时有用。
二级缓存(sessionFactory缓存),也叫应用级缓存
- 二级缓存的生命周期和sessionFactory的生命周期保持一致,可以跨session,被多个session共享,可以手动开启并指定缓存插件如ehcache,oscache等。二级缓存也只能缓存对象。二级缓存只查询主键时有用。
三级缓存(查询缓存)
- 三级缓存也叫查询缓存,查询缓存依赖二级缓存,所以在使用查询缓存之前配置好二级缓存。可以指定条件缓存
20、Hibernate核心类或接口
- Configuration接口
对Hibernate进行配置,以及对它进行启动。(加载hibernate.cfg.xml)并创建一个SessionFactory对象。 - SessionFactory接口
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建 Session对象。SessionFactory是线程安全的。 - Session接口
Session(会话)接口是Hibernate应用使用的主要接口。Session接口负责执行被持久化对象的CRUD操作(增删改查)。Session对象是非线程安全的。Session相当于jdbc的connection。 - Query接口
总之Query接口负责执行各种数据库查询。它可以使用HQL语句或SQL 语句两种表达方式。 - Transaction接口
Transaction(事务)接口是一个可选的API。负责操作相关的事务
21、Hibernate配置文件中inverse属性作用
作用:由谁来控制关联关系的自动维护
配置在<Set>
节点上面:
inverse="true"
代表被动方,由关联的对象来维护他们之间的关联关系
22、OpenSessionInView模式
主要思想是:在用户的每一次请求过程始终保持一个Session对象打开着
请求:获取Session(开启事务)→WEB→BIZ→DAO→DB
响应:DB→DAO→BIZ→WEB→提交或回滚事务(关闭Session)
<filter>
<filter-name>openSessionInView</filter-name>
<filter-class>cn.jbit.houserent.web.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring和Hibernate集成的配置方式:
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
23、Struts1和Struts2关系:
Struts2==(Struts1+WebWork)
24、简述MyBatis框架
- Mybatis和Hibernate一样,是一款开源的ORM框架的技术。
- Mybatis的机制原理:
- ①Mybatis支持普通的SQL查询、存储过程和高级映射的持久层框架。
- ②Mybatis将大量的SQL语句从程序里面剖离出来,配置在配置文件中,实现SQL的灵活配置。
25、Spring中用到的设计模式
- 工厂模式:IOC容器
- 代理模式:AOP
- 策略模式:在spring采取动态代理时,根据代理的类有无实现接口有JDK和CGLIB两种代理方式,就是采用策略模式实现的
- 单例模式:默认情况下spring中的bean只存在一个实例
26、什么是bean自动装配?
Spring容器可以自动配置相互协作beans之间的关联关系。这意味着Spring可以自动配置一个bean和其他协作bean之间的关系,通过检查BeanFactory的内容里没有使用<property>
元素。
27、解释自动装配的各种模式?
- 自动装配提供五种不同的模式供Spring容器用来自动装配beans之间的依赖注入:
- no:默认的方式是不进行自动装配,通过手工设置ref属性来进行装配bean。
- byName:通过参数名自动装配,Spring容器查找beans的属性,这些beans在XML配置文件中被设置为byName。之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
- byType:通过参数的数据类型自动自动装配,Spring容器查找beans的属性,这些beans在XML配置文件中被设置为byType。之后容器试图匹配和装配和该bean的属性类型一样的bean。如果有多个bean符合条件,则抛出错误。
- constructor:这个同byType类似,不过是应用于构造函数的参数。如果在BeanFactory中不是恰好有一个bean与构造函数参数相同类型,则抛出一个严重的错误。
- autodetect:如果有默认的构造方法,通过construct的方式自动装配,否则使用byType的方式自动装配。
28、自动装配有哪些局限性?
- 自动装配有如下局限性:
- 重写:你仍然需要使用和
<property>
设置指明依赖,这意味着总要重写自动装配。 - 原生数据类型:你不能自动装配简单的属性,如原生类型、字符串和类。
- 模糊特性:自动装配总是没有自定义装配精确,因此,如果可能尽量使用自定义装配。
- 重写:你仍然需要使用和
29、Struts2中常见的拦截器:
- params拦截器
负责将请求参数设置为Action属性 - fileUpload拦截器
对文件上传提供支持 - exception拦截器
捕获异常,并且将异常映射到用户自定义的错误页面 - validation拦截器
调用验证框架进行数据验证 - servletConfig拦截器
将源于ServletAPI的各种对象注入到Action
- struts-default.xml中定义一个defaultStack拦截器栈,并将其指定为默认拦截器
- 只要在定义包的过程中继承struts-default包,那么defaultStack将是默认的拦截器
30、Struts2中定义拦截器:
- 继承AbstractInterceptor,实现其中的intercept方法
- 配置拦截器
- interceptor定义拦截器
- interceptor-stack定义拦截器栈
- Interceptor-ref引用拦截器
- 默认的拦截器栈:defaultStack
- 引用截器
常见的案例:验证权限,错误处理等
public class UserIntercepter implements Interceptor {
@Override
public void init() {
// 初始化方法:只执行一次
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Map<String, Object> session = ActionContext.getContext().getSession();
User user = (User) session.get("user");
if (user == null) {
return "iոput";
} else {
return invocation.invoke(); // 请求通过
}
}
@Override
public void destroy() {
// 销毁方法:只执行一次
}
}
配置拦截器
<interceptors>
<!-- 自定义拦截器 -->
<interceptor name="LoginCk" class="cn.bdqn.entity.UserIntercepter"/>
<!-- 自定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="loginCk"/>
<interceptor-ref name= "defaultStack"/>
</interceptor-stack>
</interceptors>
使用拦截器
<action name="login" class="cn.bdqn.web.UserAction" method="{1}">
<result name="">/index.jsp</result>
<interceptor-ref name="myStack"/>
</action>
31、Spring事务的传播特性
- PROPAGATION_REQUIRED(propagation_required):如果存在一个事务,则支持当前事务。如果没有事务则开启
- PROPAGATION_SUPPORTS(propagation_supports):如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
- PROPAGATION_MANDATORY(propagation_mandatory):如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
- PROPAGATION_REQUIRES_NEW(propagation_requires_new):总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
- PROPAGATION_NOT_SUPPORTED(propagation_not_supported):总是非事务地执行,并挂起任何存在的事务。
- PROPAGATION_NEVER(propagation_never):总是非事务地执行,如果存在一个活动事务,则抛出异常
- PROPAGATION_NESTED(propagation_nested):如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED属性执行。
32、Spring事务的隔离级别
- ISOLATION_DEFAULT(isolation_default):这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
另外四个与JDBC的隔离级别相对应 - ISOLATION_READ_UNCOMMITTED(isolation_uncommitted):这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。 - ISOLATION_READ_COMMITTED(isolation_read_committed):保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
- ISOLATION_REPEATABLE_READ(isolation_repeatable_read):这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 - ISOLATION_SERIALIZABLE(isolation_serializable):这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
其中的一些概念的说明:
- 脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
- 不可重复读:指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
- 幻觉读:指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
33、hibernate实现多表关系的标签是什么?
- 一对一的标签为:
<one-to-one>
- 一对多的标签为:
<one-to-many>
- 多对一的标签为:
<many-to-one>
- 多对多的标签为:
<many-to-many>
34、Hibernate是如何处理事务的?
答:Hibernate的事务实际上是底层的JDBCTransaction的封装或者是JTA
Transaction的封装;默认情况下使用JDBCTransaction。
35、Hibernate的应用(Hibernate的结构)
答:
// 首先获得SessionFactory的对象
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
// 然后获得session的对象
Session session = sessionFactory.openSession();
// 其次获得Transaction的对象
Transaction tx = session.beginTransaction();
// 执行相关的数据库操作:增,删,改,查
session.save(user); // 增加,user是User类的对象
session.delete(user); // 删除
session.update(user); // 更新
Query query = session.createQuery("select * from User");//查询
List list = query.list();
// 提交事务
tx.commit();
// 如果有异常,我们还要作事务的回滚,恢复到操作之前
tx.rollback();
// 最后还要关闭session,释放资源
session.close();
36、什么是重量级?什么是轻量级?
答:轻量级是指它的创建和销毁不需要消耗太多的资源,意味着可以在程序中经常创建和销毁session的对象;重量级意味不能随意的创建和销毁它的实例,会占用很多的资源。
37、什么是SpringMVC?简单介绍下你对springMVC的理解?
Spring MVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架,它是Spring的一个模块,无需中间整合层来整合 ,它和Struts2一样都属于表现层的框架。在web模型中,MVC是一种很流行的框架,通过把Model,View,Controller分离,把较为复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
38、SpringMVC的流程?
(1)用户发送请求至前端控制器DispatcherServlet;
(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器;
(5)执行处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。
39、SpringMVC的优点
(1)它是基于组件技术的。全部的应用对象,无论控制器和视图,还是业务对象之类的都是 java组件.并且和Spring提供的其他基础结构紧密集成.
(2)不依赖于ServletAPI(目标虽是如此,但是在实现的时候确实是依赖于Servlet的)
(3)可以任意使用各种视图技术,而不仅仅局限于JSP
(4) 支持各种请求资源的映射策略
(5)它应是易于扩展的
40、SpringMVC的主要组件?
(1)前端控制器DispatcherServlet(不需要程序员开发)
作用:接收请求、响应结果相当于转发器,有了DispatcherServlet就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)处理器Handler(需要程序员开发)
(5)视图解析器ViewResolver(不需要程序员开发)
作用:进行视图的解析根据视图逻辑名解析成真正的视图(view)
(6)视图View(需要程序员开发jsp)
View是一个接口,它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
41、springMVC和struts2的区别有哪些?
(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。
42、SpringMVC怎么样设定重定向和转发的?
(1)在返回值前面加"forward:“就可以让结果转发,譬如"forward:user.do?name=method4”
(2)在返回值前面加"redirect:“就可以让返回值重定向,譬如"redirect:http://www.baidu.com”
43、SpringMvc怎么和AJAX相互调用的?
通过Jackson
框架就可以把Java
里面的对象直接转化成Js
可以识别的Json
对象。具体步骤如下:
(1)加入Jackson.jar
(2)在配置文件中配置json
的映射
(3)在接受Ajax
方法里面可以直接返回Object
,List
等,但方法前面要加上@ResponseBody
注解。
44、如何解决POST请求中文乱码问题,GET的又如何处理呢?
(1)解决post
请求乱码问题:
在web.xml
中加入:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2)get请求中文参数出现乱码解决方法有两个:
①修改tomcat
配置文件添加编码与工程编码一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
②另外一种方法对参数进行重新编码:
String userName= new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8");
ISO8859-1
是tomcat
默认编码,需要将tomcat
编码后的内容按utf-8
编码。
45、Mybatis中#{}
和${}
的区别是什么?
#{}
是预编译处理,${}
是字符串替换。
Mybatis
在处理#{}
时,会将sql中的#{}
替换为?
号,调用PreparedStatement
的set
方法来赋值;
Mybatis
在处理${}
时,就是把${}
替换成变量的值。
使用#{}
可以有效的防止SQL
注入,提高系统安全性。
46、通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id=findStudentById的MappedStatement。在Mybatis中,每一个<select>
、<insert>
、<update>
、<delete>
标签,都会被解析为一个MappedStatement对象。
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
47、Mybatis是如何进行分页的?分页插件的原理是什么?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
48、Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
第一种是使用<resultMap>
标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAMEASNAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAMEASNaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
49、Xml映射文件中,除了常见的select|insert|update|delete
标签之外,还有哪些标签?
注:这道题出自京东面试官。
还有很多其他的标签,加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind
等,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。
50、简述Mybatis的插件运行原理,以及如何编写一个插件
Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,还需要在配置文件中配置你编写的插件。
51、MyBatis一级、二级缓存
1)一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Sessionflush或close之后,该Session中的所有Cache就将清空。
2)二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。要开启二级缓存,你需要在你的SQL映射文件中添加一行:<cache/>
3)对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear。
52、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?
虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。
原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析完成了。
53、简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系?
Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。在Xml映射文件中,标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个、、、标签均会被解析为MappedStatement对象,标签内的sql会被解析为BoundSql对象。
54、Spring是什么?
Spring是一个轻量级的IoC和AOP容器框架。目的是解决企业应用开发的复杂性,使用基本的JavaBean来完成以前只可能由EJB完成的事情,并提供了更多的企业应用功能,Spring的用途不仅限于服务器端的开发,从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
55、Spring的优点?
(1)spring属于低侵入式设计,代码的污染极低;
(2)spring的DI机制降低了业务对象替换的复杂性;
(3)容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能;
(4)降低了组件之间的耦合性,实现了软件各层之间的解耦;
(5)容器提供单例模式支持;
(6)可以使用容器提供的众多服务,如事务管理,消息服务等;
(7)容器提供了众多的辅助类,能加快应用的开发;
(8)spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
(9)独立于各种应用服务器
56、Spring的AOP理解
- AOP,一般称为面向方面(切面)编程,作为面向对象的一种补充,用于解剖封装好的对象内部,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
- AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以SpringAOP为代表。
- (1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ织入到Java字节码中,运行的时候就是增强之后的AOP对象。
- (2)SpringAOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
SpringAOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:- ①JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法,当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。
- ②如果目标类没有实现接口,那么SpringAOP会选择使用CGLIB来动态代理目标类。CGLIB(CodeGenerationLibrary),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法,覆盖方法时可以添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
- (3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而SpringAOP则无需特定的编译器处理。
57、Spring的IoC理解:
(1)IOC就是控制反转。就是对象的创建权反转交给Spring,由容器控制程序之间的依赖关系,作用是实现了程序的解耦合,而非传统实现中,由程序代码直接操控。(依赖)控制权由应用代码本身转到了外部容器,由容器根据配置文件去创建实例并管理各个实例之间的依赖关系,控制权的转移,是所谓反转,并且由容器动态的将某种依赖关系注入到组件之中。BeanFactory是SpringIoC容器的具体实现与核心接口,提供了一个先进的配置机制,使得任何类型的对象的配置成为可能,用来包装和管理各种bean。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,这里用的就是java的反射机制,通过反射在运行时动态的去创建、调用对象。spring就是根据配置文件在运行时动态的去创建对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式:
第一是根据属性注入,也叫set方法注入;
第二种是根据构造方法进行注入;
第三种是根据注解进行注入。
详细的说:
(4)IoC,控制反转:将对象交给容器管理,你只需要在spring配置文件中配置相应的bean,以及设置相关的属性,让spring容器生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化以及装配好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。就是将对象的控制权反转给spring容器管理。
(5)DI机制(DependencyInjection,依赖注入):可以说是IoC的其中一个内容,在容器实例化对象的时候主动的将被调用者(或者说它的依赖对象)注入给调用对象。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
58、解释Spring支持的几种bean的作用域。
Spring容器中的bean可以分为5个范围:
(1)singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由beanfactory自身来维护。
(2)prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
(3)request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
(4)Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
(5)global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
59、Spring中bean的加载过程
(1)获取配置文件资源;
(2)对获取的xml资源进行一定的处理检验;
(3)处理包装资源;
(4)解析处理包装过后的资源;
(5)加载提取bean并注册(添加到beanDefinitionMap中)。
60、Spring框架中的单例Beans是线程安全的么?
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Springbean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如ViewModel对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。
61、描述Struts2框架(工作原理)
A.Struts2是一个典型的MVC前端的框架,它是以WebWork为核心。
B.tomcat启动的时候会加载web.xml、核心控制器FilterDispatcher会加载并解析struts.xml
C.客户端会发送一个请求到action、FilterDispatcher会根据后缀名进行拦截
D.FilterDispatcher根据struts.xml的配置文件信息找到某个action对应的某个类里的指定方法
E.执行相关的业务逻辑最后返回一个String
F.里配置name的属性值与返回的String进行匹配,跳转到指定的jsp页面
62、Hibernate配置文件中CASECADE属性作用:
配置对象之间的级联操作:
None:不级联
Save-update:保存或者更新的时候,级联操作
Delete:删除的时候级联操作
All:保存,更新或者删除都级联操作
63、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
四、数据库部分
1、分页语句MySQL和Oracle
- MySQL数据库:
select * from t order by id limit 30,10;
- Oracle数据库:
在ORACLE大数据量下的分页解决方法。一般用截取ID方法,还有是三层嵌套方法。
- 截取ID的方法
SELECT *
FROM emp a
,(SELECT empno
,rownum AS num
FROM emp) b
WHERE a.empno = b.empno
AND b.num BETWEEN 5 AND 7;
- 三层嵌套
SELECT *
FROM (SELECT a.*
,rownum r
FROM (SELECT * FROM emp) a
WHERE rownum <= 7) b
WHERE r >= 5;
- 二层嵌套
SELECT *
FROM (SELECT a.*
,rownum r
FROM emp a
WHERE rownum <= 7) b
WHERE r >= 5;
2、join与left join的区别:
inner join
(等值连接):只返回两个表中联结字段相等的行left join
(左联接):返回包括左表中的所有记录和右表中联结字段相等的记录right join
(右联接):返回包括右表中的所有记录和左表中联结字段相等的记录
3、SQL中where和having的区别
“Where”是一个约束声明,使用Where来约束来之数据库的数据,Where是在结果返回之前起作用的。
“Having”是一个过滤声明,having是对分组之后的结果筛选
4、数据库三范式
第一范式(1NF)的目标:确保每列的原子性。
第二范式(2NF)的目标:确保表中的每列,都和主键相关
第三范式(3NF)的目标:确保每列都和主键列直接相关,而不是间接相关
符合数据库三范式设计原则的数据库,可消除数据冗余、更新异常、插入异常和删除异常。
5、说出你用过的常见的关系型数据库,非关系型数据库知道哪些?
- 关系型:
- Oracle
- SQLServer
- MySQL
- DB2
- PostgreSQL
- openGauss
- 非关系型:
- Redis
- mongodb
6、存储过程和函数的区别?
存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。
7、事务是什么?
事务是作为一个逻辑单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为一个事务:
原子性事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
一致性事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如B树索引或双向链表)都必须是正确的。
隔离性由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。
持久性事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。
8、游标的作用?如何知道游标已经到了最后?
游标用于定位结果集的行,通过判断全局变量@@FETCH_STATUS可以判断是否到了最后,通常此变量不等于0表示出错或到了最后。
9、触发器?
- 触发器分为事前触发和事后触发,这两种触发有何区别?语句级触发和行级触发有何区别?
- 事前触发器运行于触发事件发生之前,而事后触发器运行于触发事件发生之后。通常事前触发器可以获取事件之前和新的字段值。
- 语句级触发器可以在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。
10、有三张表,学生表S,课程C,学生课程表SC
学生可以选修多门课程,一门课程可以被多个学生选修,通过SC表关联
1)写出建表语句;
2)写出SQL语句,查询选修了所有选修课程的学生;
3)写出SQL语句,查询选修了至少5门以上的课程的学生。
答:1)建表语句如下(mysql数据库):
create table s (
id integer primary key,
name varchar(20)
);
create table c (
id integer primary key,
name varchar(20)
);
create table sc (
sid integer references s(id),
cid integer references c(id),
primary key(sid,cid)
);
2)SQL语句如下:
SELECT stu.id
,stu.name
FROM s stu
WHERE (SELECT COUNT(*) FROM sc WHERE sid = stu.id) =
(SELECT COUNT(*) FROM c);
3)SQL语句如下:
SELECT stu.id
,stu.name
FROM s stu
WHERE (SELECT COUNT(*) FROM sc WHERE sid = stu.id) >= 5;
11、数据库表(Test)结构如下
ID NAME AGE MANAGER(所属主管人ID)
106 A 30 104
109 B 19 104
104 C 20 111
107 D 35 109
112 E 25 120
119 F 45 NULL
要求:列出所有年龄比所属主管年龄大的人的ID和名字?
答:SQL语句如下:
SELECT employee.id
,employee.name
FROM test employee
WHERE employee.age > (SELECT manager.age
FROM test manager
WHERE manager.id = employee.manager);
12、有如下两张表:表city:表state:
- city表:
CityNo | CityName | StateNo |
---|---|---|
BJ | 北京 | (Null) |
SH | 上海 | (Null) |
GZ | 广州 | GD |
DL | 大连 | LN |
- state表:
StateNo | StateName |
---|---|
GD | 广东 |
LN | 辽宁 |
得到如下结果:
CityNo | CityName | StateNo | StateName |
---|---|---|---|
BJ | 北京 | (Null) | (Null) |
DL | 大连 | LN | 辽宁 |
GZ | 广州 | GD | 广东 |
SH | 上海 | (Null) | (Null) |
- 答:SQL语句为:
SELECT c.cityno
,c.cityname
,c.stateno
,s.statename
FROM city c
,state s
WHERE c.stateno = s.stateno(+)
ORDER BY (c.cityno);
13、事务处理?
答:
- Connection类中提供了3个事务处理方法:
- setAutoCommit(Boolean autoCommit):设置是否自动提交事务,默认为自动提交事务,即为true,通过设置false禁止自动提交事务。
- commit():提交事务。
- rollback():回滚事务。
14、Java中访问数据库的步骤?Statement和PreparedStatement之间的区别?
答:Java中访问数据库的步骤如下:
1)注册驱动;
2)建立连接;
3)创建Statement;
4)执行sql语句;
5)处理结果集(若sql语句为查询语句);
6)关闭连接。
PreparedStatement被创建时即指定了SQL语句,通常用于执行多次结构相同的SQL语句。
15、用你熟悉的语言写一个连接ORACLE数据库的程序,能够完成修改和查询工作。
答:JDBC示例程序如下:
public void testJdbc() {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// step1:注册驱动;
Class.forName("oracle.jdbc.driver.OracleDriver");
// step2:获取数据库连接;
con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.39:1521:TZQDB", "tzq", "tzq@12345");
/************************ 查询 ************************/
// step3:创建Statement;
String sql = "SELECT id, fname, lname, age FROM Person_Tbl";
ps = con.prepareStatement(sql);
// step4:执行查询语句,获取结果集;
rs = ps.executeQuery();
// step5:处理结果集—输出结果集中保存的查询结果;
while (rs.next()) {
System.out.print("id=" + rs.getLong("id"));
System.out.print(",fname=" + rs.getString("fname"));
System.out.print(",lname=" + rs.getString("lname"));
System.out.print(",age=" + rs.getInt("age"));
}
/************************ JDBC修改 *********************/
sql = "UPDATE Person_Tbl SET age = 23 WHERE id = ?";
ps = con.prepareStatement(sql);
ps.setLong(1, 88);
int rows = ps.executeUpdate();
System.out.println(rows + " rows affected.");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
con.close();// 关闭数据库连接,以释放资源。
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
16、JDBC的实现分页
根据不同的数据库采用不同的sql分页语句:
- Oracle:
-- 查询从记录号2到记录号10之间的所有记录
SELECT t.*
FROM (SELECT a.*
,rownum r
FROM t_user a) t
WHERE t.r BETWEEN 2 AND 10
- Mysql:
SELECT *
FROM t_user
LIMIT 2, 10;
17、一条sql执行过长的时间,你如何优化,从哪些方面?
答:
1、查看sql是否涉及多表的联表或者子查询,如果有,看是否能进行业务拆分,相关字段冗余或者合并成临时表(业务和算法的优化)
2、涉及链表的查询,是否能进行分表查询,单表查询之后的结果进行字段整合
3、如果以上两种都不能操作,非要链表查询,那么考虑对相对应的查询条件做索引。加快查询速度
4、针对数量大的表进行历史表分离(如交易流水表)
5、数据库主从分离,读写分离,降低读写针对同一表同时的压力,至于主从同步,mysql有自带的binlog实现主从同步
6、explain分析sql语句,查看执行计划,分析索引是否用上,分析扫描行数等等
7、查看mysql执行日志,看看是否有其他方面的问题
18、MySQL的事物隔离级别?
答:
Mysql的事物隔离级别其实跟Spring的事物隔离级别一样,都是
1、ReadUncommitted(读取未提交内容),
2、ReadCommitted(读取提交内容),
3、RepeatableRead(可重读),
4、Serializable(可串行化)
19、数据库连接池基本概念及原理
对于共享资源,有一个很著名的设计模式:资源池(ResourcePool)。该模式正是为了解决资源的频繁分配?释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量、使用情况,为系统开发、测试及性能调整提供依据。
20、连接池技术点?
1、并发问题
为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为Java语言自身提供了对并发管理的支持,使用synchronized
关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized
关键字,如:
public synchronized Connection getConnection()
2、多数据库服务器和多用户
对于大型的企业级应用,常常需要同时连接不同的数据库(如连接Oracle和Sybase)。如何连接不同的数据库呢?我们采用的策略是:设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的url地址、用户名、密码等信息。
如:tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。
根据资源文件提供的信息,创建多个连接池类的实例,每一个实例都是一个特定数据库的连接池。连接池管理类实例为每个连接池实例取一个名字,通过不同的名字来管理不同的连接池。
对于同一个数据库有多个用户使用不同的名称和密码访问的情况,也可以通过资源文件处理,即在资源文件中设置多个具有相同url地址,但具有不同用户名和密码的数据库连接信息
3、事务处理
事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。
在Java语言中,Connection类本身提供了对事务的支持,可以通过设置Connection的AutoCommit属性为false,然后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用,就必须提供相应的事务支持机制。可采用每一个事务独占一个连接来实现,这种方法可以大大降低事务管理的复杂性。
-
连接池的分配与释放
连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时,系统首先检查空闲池内有没有空闲连接。如果有就把建立时间最长(通过容器的顺序存放实现)的那个连接分配给他(实际是先做连接是否有效的判断,如果可用就分配给用户,如不可用就把这个连接从空闲池删掉,重新检测空闲池是否还有连接);如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数(maxConn),如果没有达到,就新建一个连接,如果已经达到,就等待一定的时间(timeout)。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户,如果等待时间超过预定时间timeout,则返回空值(null)。系统对已经分配出去正在使用的连接只做计数,当使用完后再返还给空闲池。对于空闲连接的状态,可开辟专门的线程定时检测,这样会花费一定的系统开销,但可以保证较快的响应速度。也可采取不开辟专门线程,只是在分配前检测的方法。 -
连接池的配置与维护
连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConn)和最大连接数(maxConn)来控制连接池中的连接。最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过反复测试,找到最佳点。 -
如何确保连接池中的最小连接数呢?
有动态和静态两种策略。
动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。
21、连接池的实现?
连接池模型
连接池包括一个连接池类(DBConnectionPool)和一个连接池管理类(DBConnetionPoolManager)。连接池类是对某一数据库所有连接的“缓冲池”,主要实现以下功能:①从连接池获取或创建可用连接;②使用完毕之后,把连接返还给连接池;③在系统关闭前,断开所有连接并释放连接占用的系统资源;④还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题),并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。
连接池管理类是连接池类的外覆类(wrapper),符合单例模式,即系统中只能有一个连接池管理类的实例。其主要用于对多个连接池对象的管理,具有以下功能:①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息,创建连接池对象;③为方便管理多个连接池对象,为每一个连接池对象取一个名字,实现连接池名字与其实例之间的映射;④跟踪客户使用连接情况,以便需要是关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理,如系统需要连接不同的数据库,或连接相同的数据库但由于安全性问题,需要不同的用户使用不同的名称和密码。
连接池实现
下面给出连接池类和连接池管理类的主要属性及所要实现的基本接口:
连接池类DBConnectionPool
代码:
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Timer;
public class DBConnectionPool {
private int checkedOut;// 已被分配出去的连接数
private ArrayList freeConnections = new ArrayList();
// 容器,空闲池,根据//创建时间顺序存放已创建但尚未分配出去的连接
private int minConn;// 连接池里连接的最小数量
private int maxConn;// 连接池里允许存在的最大连接数
private String name;// 为这个连接池取个名字,方便管理
private String password;// 连接数据库时需要的密码
private String url;// 所要创建连接的数据库的地址
private String user;// 连接数据库时需要的用户名
public Timer timer;// 定时器
// 公开的构造函数
public DBConnectionPool(String name, String URL, String user, String password, int maxConn) {
}
// 使用完毕之后,把连接返还给空闲池
public synchronized void freeConnection(Connection con) {
}
// 得到一个连接,timeout是等待时间
public synchronized Connection getConnection(long timeout) {
return null;
}
// 断开所有连接,释放占用的系统资源
public synchronized void release() {
}
// 新建一个数据库连接
private Connection newConnection() {
return null;
}
public synchronized void TimerEvent() {
}
}
连接池管理类DBConnectionPool
代码:
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
public class DBConnectionManager {
// 连接池管理类的唯一实例
static private DBConnectionManager instance;
// 客户数量
static private int clients;
// 容器,存放数据库驱动程序
private ArrayList drivers = new ArrayList();
// 以name/value的形式存取连接池对象的名字及连接池对象
private HashMap pools = new HashMap();
/**
* 如果唯一的实例instance已经创建,直接返回这个实例;否则,调用私有构造函数, 创建连接池管理类的唯一实例
*/
static synchronized public DBConnectionManager getInstance() {
return null;
}
// 私有构造函数,在其中调用初始化函数init()
private DBConnectionManager() {
}
// 释放一个连接,name是一个连接池对象的名字
public void freeConnection(String name, Connection con) {
}
// 从名字为name的连接池对象中得到一个连接
public Connection getConnection(String name) {
}
// 从名字为name的连接池对象中取得一个连接,time是等待时间
public Connection getConnection(String name, long time) {
}
// 释放所有资源
public synchronized void release() {
}
// 根据属性文件提供的信息,创建一个或多个连接池
private void createPools(Properties props) {
}
// 初始化连接池管理类的唯一实例,由私有构造函数调用
private void init() {
}
// 装载数据库驱动程序
private void loadDrivers(Properties props) {
}
}
五、程序算法
1、杨辉三角
java代码:
public class YanghuiTriangle {
public static void main(String[] args) {
int i, j, n = 8, l, r;
int a[] = new int[9];
System.out.println(1);
for (i = 1; i <= n; i++) {
l = 1;
System.out.print(1 + " ");
for (j = 1; j <= i; j++) {
r = a[j];
a[j] = l + r;/* 每个数是上面两数之和 */
l = r;
System.out.print(a[j] + " ");
}
System.out.println();
}
}
}
python代码:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def pascal_triangle(n):
triangle = []
for row_num in range(n):
# The first element of each row is always 1
row = [1]
if triangle:
last_row = triangle[-1]
# Each element (except the first and last) is the sum of the two elements above it
row.extend([sum(pair) for pair in zip(last_row, last_row[1:])])
# The last element of each row is also 1
row.append(1)
triangle.append(row)
return triangle
# 打印前5行的杨辉三角
for row in pascal_triangle(9):
print(' '.join(map(str, row)).center(50))
2、写一个函数计算当参数为N的值:1-2+3-4+5-6+7……+N
java代码:
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入数字n:");
int n = input.nextInt();
int sum = 0;
String msg = "";
for (int i = 1; i <= n; i++) {
int k = i;
if (i % 2 == 0) {
k = -k;
}
sum = sum + k;
if (k > 1) {
msg = msg + "+" + k;
} else {
msg = msg + k;
}
}
System.out.println(msg + "=" + sum);
}
}
执行结果:
请输入数字n:10
1-2+3-4+5-6+7-8+9-10=-5
3、在ABCDEF六人中随机抽取3人中奖,要求中奖人不能重复
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (char i = 'A'; i <= 'F'; i++) {
list.add("" + i);
}
for (int k = 0; k < 3; k++) {
int d = (int) (Math.random() * (6 - k));
System.out.println(list.remove(d));
}
}
}
4、排序都有哪几种方法?请列举。用JAVA实现一个快速排序?
答:排序的方法有:插入排序(直接插入排序、希尔排序),交换排序(冒泡排序、快速排序),选择排序(直接选择排序、堆排序),归并排序,分配排序(箱排序、基数排序);快速排序的伪代码:
//使用快速排序方法对a[0:n-1]排序
从a[0:n-1]中选择一个元素作为middle,该元素为支点;
把余下的元素分割为两段left和right,使得left中的元素都小于等于支点,
而right中的元素都大于等于支点;
递归地使用快速排序方法对left进行排序;
递归地使用快速排序方法对right进行排序;
所得结果为left+middle+right。
5、写一个一小段程序检查数字是否为质数;以上的程序你采用的哪种语言写的?采用该种语言的理由是什么?
答:代码如下:
c++/c:
#include<math.h>
bool prime(int n){
if(n<=0)exit(0);
for(int i=2;i<=n;i++)
for(int j=2;j<=sqrt(i);j++)
if((n%j==0)&&(j!=n))
return false;
return true;
java:
public class PrimeChecker {
public static boolean isPrime(int num) {
// 0和1不是质数
if (num <= 1) {
return false;
}
// 2是最小的质数
if (num == 2) {
return true;
}
// 排除所有偶数
if (num % 2 == 0) {
return false;
}
// 只需要检查到sqrt(num),因为如果n=a*b,那么a和b中至少有一个小于等于sqrt(n)
for (int i = 3; i * i <= num; i += 2) {
if (num % i == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
int number = 29;
System.out.println(number + " 是质数吗? " + isPrime(number));
}
}
6、设有n个人依围成一圈,从第1个人开始报数
数到第m个人出列,然后从出列的下一个人开始报数,数到第m个人又出列,…,如此反复到所有的人全部出列为止。设n个人的编号分别为1,2,…,n,打印出出列的顺序;要求用java实现。
public class CountGame {
private static boolean same(int[] p, int l, int n) {
for (int i = 0; i < l; i++) {
if (p[i] == n) {
return true;
}
}
return false;
}
public static void play(int playerNum, int step) {
int[] p = new int[playerNum];
int counter = 1;
while (true) {
if (counter > playerNum * step) {
break;
}
for (int i = 1; i < playerNum + 1; i++) {
while (true) {
if (same(p, playerNum, i) == false)
break;
else
i = i + 1;
}
if (i > playerNum)
break;
if (counter % step == 0) {
System.out.print(i + " ");
p[counter / step - 1] = i;
}
counter += 1;
}
}
System.out.println();
}
public static void main(String[] args) {
play(10, 7);
}
}
7、写一个方法1000的阶乘
java
代码如下:
import java.math.BigInteger;
public class Factorial {
public static void main(String[] args) {
int number = 1000;
BigInteger factorial = calculateFactorial(number);
System.out.println("Factorial of " + number + " is: " + factorial);
}
public static BigInteger calculateFactorial(int n) {
BigInteger result = BigInteger.ONE;
for (int i = 2; i <= n; i++) {
result = result.multiply(BigInteger.valueOf(i));
}
return result;
}
}
python
代码如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def factorial(n):
if n == 0 or n == 1:
return 1
else:
result = 1
for i in range(2, n + 1):
result *= i
return result
# 计算1000的阶乘
result = factorial(1000)
print(result)
8、菲波拉契数列
斐波那契数列(Fibonacci Sequence)是一个著名的数列,其中每一项都是前两项之和,通常从 0 和 1 开始。其定义如下:
- ( F(0) = 0 )
- ( F(1) = 1 )
- ( F(n) = F(n-1) + F(n-2) ) (对于 ( n \geq 2 ))
斐波那契数列的前几项是:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …
java代码如下:
import java.util.ArrayList;
public class Fibonacci {
public static void main(String[] args) {
int n = 10;
ArrayList<Integer> fibSequence = new ArrayList<>();
fibSequence.add(0);
fibSequence.add(1);
for (int i = 2; i < n; i++) {
fibSequence.add(fibSequence.get(i - 1) + fibSequence.get(i - 2));
}
System.out.println(fibSequence);
}
}
python
代码如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def fibonacci(n):
fib_sequence = [0, 1]
while len(fib_sequence) < n:
fib_sequence.append(fib_sequence[-1] + fib_sequence[-2])
return fib_sequence
# 生成前 10 项
print(fibonacci(10))
9、素数
- 题目:判断101-200之间有多少个素数,并输出所有素数。
- 程序分析:判断一个数是否为素数(质数)的基本方法是检查它是否能被 2 到其平方根(sqrt)之间的任何整数整除。如果能被整除,则表明此数不是素数,反之是素数。
java代码如下:
public class PrimeNumbers {
public static void main(String[] args) {
int start = 101;
int end = 200;
int primeCount = 0;
System.out.println("Prime numbers between " + start + " and " + end + " are:");
for (int i = start; i <= end; i++) {
if (isPrime(i)) {
System.out.print(i + " ");
primeCount++;
}
}
System.out.println("\nTotal number of prime numbers between " + start + " and " + end + ": " + primeCount);
}
public static boolean isPrime(int num) {
if (num <= 1) {
return false;
}
for (int i = 2; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
}
程序输出结果:
Prime numbers between 101 and 200 are:
101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199
Total number of prime numbers between 101 and 200: 21
10、水仙花数
“水仙花数”(Narcissistic number,也称为自恋数、自幂数、阿姆斯特朗数)是指一个 n 位数,其各个位上的数字的 n 次幂之和等于该数本身。例如,153 是一个水仙花数,因为 1^3 + 5^3 + 3^3 = 153。
- 题目:打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个"水仙花数",因为153=1的三次方+5的三次方+3的三次方。
以下是一个用 Java 编写的程序,用于打印出所有的三位水仙花数:
public class NarcissisticNumber {
public static void main(String[] args) {
System.out.println("Three-digit narcissistic numbers are:");
for (int i = 100; i < 1000; i++) {
if (isNarcissistic(i)) {
System.out.println(i);
}
}
}
public static boolean isNarcissistic(int num) {
int originalNum = num;
int sum = 0;
int digits = String.valueOf(num).length();
while (num > 0) {
int digit = num % 10;
sum += Math.pow(digit, digits);
num /= 10;
}
return sum == originalNum;
}
}
11、分解质因数
题目:将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5
。
程序分析:对n进行分解质因数,应先找到一个最小的质数k,然后按下述步骤完成:
(1)如果这个质数恰等于n,则说明分解质因数的过程已经结束,打印出即可。
(2)如果n<>k,但n能被k整除,则应打印出k的值,并用n除以k的商,作为新的正整数你n,重复执行第一步。
(3)如果n不能被k整除,则用k+1作为k的值,重复执行第一步。
步骤
- 初始化:从最小的质数2开始。
- 除法:如果当前质数可以整除目标数,则将其作为一个质因数,并继续用该质数除目标数,直到不能整除为止。
- 递增质数:如果当前质数不能整除目标数,则递增质数,继续步骤2。
- 终止条件:当目标数变为1时,分解完成。
import java.util.ArrayList;
import java.util.List;
public class PrimeFactorization {
public static List<Integer> primeFactors(int n) {
List<Integer> factors = new ArrayList<>();
// 从最小的质数2开始
int divisor = 2;
while (n > 1) {
while (n % divisor == 0) {
factors.add(divisor);
n /= divisor;
}
divisor++;
}
return factors;
}
public static void main(String[] args) {
int number = 90;
List<Integer> factors = primeFactors(number);
System.out.println(number + " 的质因数分解结果为: " + factors);
}
}
优化
上述方法虽然简单,但在处理大数时效率较低。可以通过以下优化提高效率:
- 跳过偶数:除了2以外,所有偶数都不是质数,可以跳过。
- 只检查到平方根:如果目标数 n 没有小于等于其平方根的质因数,那么 n 本身就是一个质数。
以下是优化后的Java代码:
import java.util.ArrayList;
import java.util.List;
public class PrimeFactorization {
public static List<Integer> primeFactorsOptimized(int n) {
List<Integer> factors = new ArrayList<>();
// 处理2的因数
while (n % 2 == 0) {
factors.add(2);
n /= 2;
}
// 处理奇数因数
for (int i = 3; i <= Math.sqrt(n); i += 2) {
while (n % i == 0) {
factors.add(i);
n /= i;
}
}
// 如果n是质数且大于2
if (n > 2) {
factors.add(n);
}
return factors;
}
public static void main(String[] args) {
int number = 90;
List<Integer> factors = primeFactorsOptimized(number);
System.out.println(number + " 的质因数分解结果为: " + factors);
}
}
总结
通过上述步骤和代码,可以将一个正整数分解为质因数。优化后的代码在处理大数时效率更高,适用于更广泛的应用场景。 在Java中,使用ArrayList来存储质因数,并通过循环和条件判断实现质因数分解。
12、最大公约数和最小公倍数
题目:输入两个正整数m和n,求其最大公约数和最小公倍数。
/**
在循环中,只要除数不等于0,用较大数除以较小的数,将小的一个数作为下一轮循环的大数,取得的余数作为下一轮循环的较小的数,如此循环直到较小的数的值为0,返回较大的数,此数即为最大公约数,最小公倍数为两数之积除以最大公约数。
*/
import java.util.Scanner;
public class GCDLCM {
public static void main(String[] args) {
int a, b, m;
Scanner s = new Scanner(System.in);
System.out.print("键入一个整数:");
a = s.nextInt();
System.out.print("再键入一个整数:");
b = s.nextInt();
deff cd = new deff();
m = cd.deff(a, b);
int n = a * b / m;
System.out.println("最大公约数:" + m);
System.out.println("最小公倍数:" + n);
}
}
class deff {
public int deff(int x, int y) {
int t;
if (x < y) {
t = x;
x = y;
y = t;
}
while (y != 0) {
if (x == y)
return x;
else {
int k = x % y;
x = y;
y = k;
}
}
return x;
}
}
13、输入一行字符分别统计出其中英文字母、空格、数字和其它字符的个数
import java.util.Scanner;
public class CharacterCounter {
public static void main(String[] args) {
int abcCount = 0;// 英文字母个数
int spaceCount = 0;// 空格键个数
int numCount = 0;// 数字个数
int otherCount = 0;// 其他字符个数
Scanner scan = new Scanner(System.in);// 扫描器接受控制台的输入信息
System.out.println("输入一组字符");
String str = scan.nextLine();// 取出控制台的一行信息,也就是你输入的信息
char[] ch = str.toCharArray();// 把取道的字符串变成一个char数组
for (int i = 0; i < ch.length; i++) {
if (Character.isLetter(ch[i])) {
// 判断是否字母
abcCount++;
} else if (Character.isDigit(ch[i])) {
// 判断是否数字
numCount++;
} else if (Character.isSpaceChar(ch[i])) {
// 判断是否空格键
spaceCount++;
} else {
// 以上都不是则认为是其他字符
otherCount++;
}
}
System.out.println("字母个数:" + abcCount);
System.out.println("数字个数:" + numCount);
System.out.println("空格个数:" + spaceCount);
System.out.println("其他字符个数:" + otherCount);
}
}
14、完数
完数(Perfect Number)是指一个数恰好等于它的所有真因子(即除了自身的因子)之和。例如,6是一个完数,因为6 = 1 + 2 + 3。
下面是一个Java程序,用于找出1000以内的所有完数:
public class PerfectNumber {
public static void main(String[] args) {
System.out.println("1000以内的完数有:");
for (int i = 1; i <= 1000; i++) {
if (isPerfectNumber(i)) {
System.out.println(i);
}
}
}
public static boolean isPerfectNumber(int number) {
int sum = 0;
for (int i = 1; i <= number / 2; i++) {
if (number % i == 0) {
sum += i;
}
}
return sum == number;
}
}
这个程序首先定义了一个isPerfectNumber
方法,用于判断一个数是否为完数。然后在main
方法中,遍历1到1000之间的所有整数,对每个整数调用isPerfectNumber
方法,如果返回true
,则打印该数。
运行这个程序,你会得到1000以内的所有完数。根据定义,1000以内的完数有:6, 28, 496。
15、一球从100米高度自由落下,每次落地后反跳回原高度
一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在 第10次落地时,共经过多少米?第10次反弹多高?
public class BallBounce {
public static void run1() {
double initialHeight = 100.0; // 初始高度
double totalDistance = 0.0; // 总距离
double currentHeight = initialHeight; // 当前高度
for (int i = 1; i <= 10; i++) {
// 落地距离
totalDistance += currentHeight;
// 计算反弹高度
currentHeight /= 2;
// 如果是偶数次落地,加上反弹的距离
if (i % 2 == 0) {
totalDistance += currentHeight;
}
}
// 输出结果
System.out.println("第10次落地时,共经过的距离:" + totalDistance + "米");
System.out.println("第10次反弹的高度:" + currentHeight + "米");
}
public static void run2() {
double h = 100, s = 0;
for (int i = 1; i <= 10; i++) {
s = s + 2 * h;
h = h / 2;
}
s = s - 100;
System.out.println("经过路程:" + s);
System.out.println("最后高度:" + h);
}
public static void main(String[] args) {
run1();
run2();
}
}
16、一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
public class SpecialInteger {
public static void main(String[] args) {
for (int i = 1; i <= 10000; i++) {
if (isPerfectSquare(i + 100) && isPerfectSquare(i + 168)) {
System.out.println("满足条件的整数是:" + i);
break;
}
}
}
public static boolean isPerfectSquare(int number) {
int sqrt = (int) Math.sqrt(number);
return sqrt * sqrt == number;
}
}
方式二:
public static void run3() {
for (int x = 1; x < 100000; x++) {
if (Math.sqrt(x + 100) % 1 == 0) {
if (Math.sqrt(x + 168) % 1 == 0) {
System.out.println(x + "加100是一个完全平方数,再加168又是一个完全平方数");
}
}
}
}
17、输入某年某月某日,判断这一天是这一年的第几天
java代码示例1:
import java.util.Scanner;
public class DayOfYear {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入年份:");
int year = scanner.nextInt();
System.out.print("请输入月份:");
int month = scanner.nextInt();
System.out.print("请输入日期:");
int day = scanner.nextInt();
int dayOfYear = calculateDayOfYear(year, month, day);
System.out.println(year + "年" + month + "月" + day + "日是这一年的第" + dayOfYear + "天。");
}
public static int calculateDayOfYear(int year, int month, int day) {
// 每个月的天数,平年
int[] daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// 判断是否为闰年,如果是闰年,2月有29天
if (isLeapYear(year)) {
daysInMonth[1] = 29;
}
// 计算前几个月的总天数加上当前月的天数
int dayOfYear = 0;
for (int i = 0; i < month - 1; i++) {
dayOfYear += daysInMonth[i];
}
dayOfYear += day;
return dayOfYear;
}
public static boolean isLeapYear(int year) {
if (year % 4 == 0) {
if (year % 100 == 0) {
return year % 400 == 0;
} else {
return true;
}
} else {
return false;
}
}
}
java代码示例2:
import java.util.Scanner;
public class DayOfYear {
public static void main(String[] args) {
int year, month, day;
int days = 0;
int d = 0;
int e;
input fymd = new input();
do {
e = 0;
System.out.print("输入年:");
year = fymd.input();
System.out.print("输入月:");
month = fymd.input();
System.out.print("输入天:");
day = fymd.input();
if (year < 0 || month < 0 || month > 12 || day < 0 || day > 31) {
System.out.println("输入错误,请重新输入!");
e = 1;
}
} while (e == 1);
for (int i = 1; i < month; i++) {
switch (i) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days = 31;
break;
case 4:
case 6:
case 9:
case 11:
days = 30;
break;
case 2:
if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) {
days = 29;
} else {
days = 28;
}
break;
}
d += days;
}
System.out.println(year + "-" + month + "-" + day + "是这年的第" + (d + day) + "天。");
}
}
class input {
public int input() {
int value = 0;
Scanner s = new Scanner(System.in);
value = s.nextInt();
return value;
}
}
18、输入三个整数x,y,z,请把这三个数由小到大输出
下面是一个Java程序,用于输入三个整数 x
, y
, z
,并将这三个数由小到大输出:
import java.util.Scanner;
public class SortThreeNumbers {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入第一个整数 x:");
int x = scanner.nextInt();
System.out.print("请输入第二个整数 y:");
int y = scanner.nextInt();
System.out.print("请输入第三个整数 z:");
int z = scanner.nextInt();
int[] numbers = { x, y, z };
sortArray(numbers);
System.out.println("由小到大的顺序是:" + numbers[0] + " " + numbers[1] + " " + numbers[2]);
}
public static void sortArray(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
// 交换 array[j] 和 array[j + 1]
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
}
这个程序首先通过 Scanner
类获取用户输入的三个整数 x
, y
, z
。然后将这三个数存储在一个数组 numbers
中。接下来,调用 sortArray
方法对数组进行排序。sortArray
方法使用了冒泡排序算法,将数组中的元素由小到大排序。
最后,程序输出排序后的数组,即三个整数由小到大的顺序。
运行这个程序,输入三个整数,程序会输出它们由小到大的顺序。例如:
请输入第一个整数 x:3
请输入第二个整数 y:1
请输入第三个整数 z:2
由小到大的顺序是:1 2 3
这个程序使用了冒泡排序算法,对于三个元素的数组来说,效率已经足够。如果需要处理更多元素,可以考虑使用更高效的排序算法,例如快速排序或归并排序。不过对于这个简单的任务,冒泡排序已经足够。
19、计算字符串中子串出现的次数
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.print("请输入字符串:");
String str1 = s.nextLine();
System.out.print("请输入子串:");
String str2 = s.nextLine();
int count = 0;
if (str1.equals("") || str2.equals("")) {
System.out.println("你没有输入字符串或子串,无法比较!");
System.exit(0);
} else {
for (int i = 0; i <= str1.length() - str2.length(); i++) {
if (str2.equals(str1.substring(i, str2.length() + i)))
// 这种比法有问题,会把"aaa"看成有2个"aa"子串。
count++;
}
System.out.println("子串在字符串中出现:" + count + "次");
}
}
}
20、两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。
答案:
public class Practise {
static char[] m = { 'a', 'b', 'c' };
static char[] n = { 'x', 'y', 'z' };
public static void main(String[] args) {
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < n.length; j++) {
if (m[i] == 'a' && n[j] == 'x') {
continue;
} else if (m[i] == 'a' && n[j] == 'y') {
continue;
} else if ((m[i] == 'c' && n[j] == 'x') || (m[i] == 'c' && n[j] == 'z')) {
continue;
} else if ((m[i] == 'b' && n[j] == 'z') || (m[i] == 'b' && n[j] == 'y')) {
continue;
} else
System.out.println(m[i] + " vs " + n[j]);
}
}
}
}
结果:
a vs z
b vs x
c vs y
21、打印出如下图案(菱形)
*
***
*****
*******
*****
***
*
public class Practise {
public static void main(String[] args) {
int H = 7, W = 7;// 高和宽必须是相等的奇数
for (int i = 0; i < (H + 1) / 2; i++) {
for (int j = 0; j < W / 2 - i; j++) {
System.out.print(" ");
}
for (int k = 1; k < (i + 1) * 2; k++) {
System.out.print('*');
}
System.out.println();
}
for (int i = 1; i <= H / 2; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(" ");
}
for (int k = 1; k <= W - 2 * i; k++) {
System.out.print('*');
}
System.out.println();
}
}
}
22、有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13…求出这个数列的前20项之和。
public class Practise {
public static void main(String[] args) {
int x = 2, y = 1, t;
double sum = 0;
for (int i = 1; i <= 20; i++) {
sum = sum + (double) x / y;
t = y;
y = x;
x = y + t;
}
System.out.println("前20项相加之和是:" + sum);
}
}
23、求1+2!+3!+…+20!的和
public class Practise {
public static void main(String[] args) {
long sum = 0;
long fac = 1;
for (int i = 1; i <= 20; i++) {
fac = fac * i;
sum += fac;
}
System.out.println(sum);
}
}
24、利用递归方法求5!
public class Practise {
public static void main(String[] args) {
int n = 5;
rec fr = new rec();
System.out.println(n + "!=" + fr.rec(n));
}
}
class rec {
public long rec(int n) {
long value = 0;
if (n == 1) {
value = 1;
} else {
value = n * rec(n - 1);
}
return value;
}
}
25、有5个人坐在一起,问第五个人多少岁
题目:有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?
public class Practise {
public static void main(String[] args) {
int age = 10;
for (int i = 2; i <= 5; i++) {
age = age + 2;
}
System.out.println(age);
}
}
26、给一个不多于5位的正整数
题目:给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。
//使用了长整型最多输入18位
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.print("请输入一个正整数:");
long a = s.nextLong();
String ss = Long.toString(a);
char[] ch = ss.toCharArray();
int j = ch.length;
System.out.println(a + "是一个" + j + "位数。");
System.out.print("按逆序输出是:");
for (int i = j - 1; i >= 0; i--) {
System.out.print(ch[i]);
}
}
}
27、一个5位数,判断它是不是回文数
题目:一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
boolean is = true;
System.out.print("请输入一个正整数:");
long a = s.nextLong();
String ss = Long.toString(a);
char[] ch = ss.toCharArray();
int j = ch.length;
for (int i = 0; i < j / 2; i++) {
if (ch[i] != ch[j - i - 1]) {
is = false;
}
}
if (is == true) {
System.out.println("这是一个回文数");
} else {
System.out.println("这不是一个回文数");
}
}
}
28、请输入星期几的第一个字母来判断一下是星期几
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
getChar tw = new getChar();
System.out.println("请输入星期的第一个大写字母:");
char ch = tw.getChar();
switch (ch) {
case 'M':
System.out.println("Monday");
break;
case 'W':
System.out.println("Wednesday");
break;
case 'F':
System.out.println("Friday");
break;
case 'T': {
System.out.println("请输入星期的第二个字母:");
char ch2 = tw.getChar();
if (ch2 == 'U') {
System.out.println("Tuesday");
} else if (ch2 == 'H') {
System.out.println("Thursday");
} else {
System.out.println("无此写法!");
}
}
;
break;
case 'S': {
System.out.println("请输入星期的第二个字母:");
char ch2 = tw.getChar();
if (ch2 == 'U') {
System.out.println("Sunday");
} else if (ch2 == 'A') {
System.out.println("Saturday");
} else {
System.out.println("无此写法!");
}
}
;
break;
default:
System.out.println("无此写法!");
}
}
}
class getChar {
public char getChar() {
Scanner s = new Scanner(System.in);
String str = s.nextLine();
char ch = str.charAt(0);
if (ch < 'A' || ch > 'Z') {
System.out.println("输入错误,请重新输入");
ch = getChar();
}
return ch;
}
}
29、对10个数进行排序
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int[] a = new int[10];
System.out.println("请输入10个整数:");
for (int i = 0; i < 10; i++) {
a[i] = s.nextInt();
}
for (int i = 0; i < 10; i++) {
for (int j = i + 1; j < 10; j++) {
if (a[i] > a[j]) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
}
for (int i = 0; i < 10; i++) {
System.out.print(a[i] + " ");
}
}
}
30、求一个3*3矩阵对角线元素之和
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int[][] a = new int[3][3];
System.out.println("请输入9个整数:");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
a[i][j] = s.nextInt();
}
}
System.out.println("输入的3*3矩阵是:");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.print(a[i][j] + " ");
}
System.out.println();
}
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) {
sum += a[i][j];
}
}
}
System.out.println("对角线之和是:" + sum);
}
}
31、有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
import java.util.Arrays;
public class InsertIntoSortedArray {
public static void main(String[] args) {
int[] sortedArray = {1, 3, 5, 7, 9};
int numberToInsert = 6;
System.out.println("原数组: " + Arrays.toString(sortedArray));
int[] newArray = insertIntoSortedArray(sortedArray, numberToInsert);
System.out.println("插入后的数组: " + Arrays.toString(newArray));
}
public static int[] insertIntoSortedArray(int[] array, int number) {
int[] newArray = new int[array.length + 1];
int i = 0;
// 找到插入位置
while (i < array.length && array[i] < number) {
newArray[i] = array[i];
i++;
}
// 插入新数
newArray[i] = number;
// 复制剩余元素
while (i < newArray.length) {
newArray[i + 1] = array[i];
i++;
}
return newArray;
}
}
32、取一个整数a从右端开始的4~7位
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.print("请输入一个7位以上的正整数:");
long a = s.nextLong();
String ss = Long.toString(a);
char[] ch = ss.toCharArray();
int j = ch.length;
if (j < 7) {
System.out.println("输入错误!");
} else {
System.out.println("截取从右端开始的4~7位是:" + ch[j - 7] + ch[j - 6] + ch[j - 5] + ch[j - 4]);
}
}
}
33、求s=a+aa+aaa+aaaa+aa…a的值
题目:求s=a+aa+aaa+aaaa+aa…a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
long a, b = 0, sum = 0;
Scanner s = new Scanner(System.in);
System.out.print("输入数字a的值:");
a = s.nextInt();
System.out.print("输入相加的项数:");
int n = s.nextInt();
int i = 0;
while (i < n) {
b = b + a;
sum = sum + b;
a = a * 10;
++i;
}
System.out.println(sum);
}
}
34、打印出杨辉三角形(要求打印出10行如下图)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
java代码:
public class Practise {
public static void main(String[] args) {
int[][] a = new int[10][10];
for (int i = 0; i < 10; i++) {
a[i][i] = 1;
a[i][0] = 1;
}
for (int i = 2; i < 10; i++) {
for (int j = 1; j < i; j++) {
a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
}
}
for (int i = 0; i < 10; i++) {
for (int k = 0; k < 2 * (10 - i) - 1; k++) {
System.out.print(" ");
}
for (int j = 0; j <= i; j++) {
System.out.print(a[i][j] + " ");
}
System.out.println();
}
}
}
35、输入3个数a,b,c,按大小顺序输出
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("请输入3个整数:");
int a = s.nextInt();
int b = s.nextInt();
int c = s.nextInt();
if (a < b) {
int t = a;
a = b;
b = t;
}
if (a < c) {
int t = a;
a = c;
c = t;
}
if (b < c) {
int t = b;
b = c;
c = t;
}
System.out.println("从大到小的顺序输出:");
System.out.println(a + " " + b + " " + c);
}
}
36、有1、2、3、4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位数?
题目:有1、2、3、4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位数?并把他们都输出。
public class Practise {
public static void main(String[] args) {
int count = 0;
for (int x = 1; x < 5; x++) {
for (int y = 1; y < 5; y++) {
for (int z = 1; z < 5; z++) {
if (x != y && y != z && x != z) {
count++;
System.out.println(x * 100 + y * 10 + z);
}
}
}
}
System.out.println("共有" + count + "个三位数");
}
}
37、企业发放的奖金根据利润提成
题目:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%;60万到100万之间时,高于60万元的部分,可提成1.5%,高于100万元时,超过100万元的部分按1%提成,从键盘输入当月利润,求应发放奖金总数?
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
double x = 0, y = 0;
System.out.print("输入当月利润(万):");
Scanner s = new Scanner(System.in);
x = s.nextInt();
if (x > 0 && x <= 10) {
y = x * 0.1;
} else if (x > 10 && x <= 20) {
y = 10 * 0.1 + (x - 10) * 0.075;
} else if (x > 20 && x <= 40) {
y = 10 * 0.1 + 10 * 0.075 + (x - 20) * 0.05;
} else if (x > 40 && x <= 60) {
y = 10 * 0.1 + 10 * 0.075 + 20 * 0.05 + (x - 40) * 0.03;
} else if (x > 60 && x <= 100) {
y = 20 * 0.175 + 20 * 0.05 + 20 * 0.03 + (x - 60) * 0.015;
} else if (x > 100) {
y = 20 * 0.175 + 40 * 0.08 + 40 * 0.015 + (x - 100) * 0.01;
}
System.out.println("应该提取的奖金是" + y + "万");
}
}
结果:
输入当月利润(万):60
应该提取的奖金是3.35万
38、猴子吃桃问题
题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下 的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。
public class Practise {
public static void main(String[] args) {
int x = 1;
for (int i = 2; i <= 10; i++) {
x = (x + 1) * 2;
}
System.out.println("猴子第一天摘了" + x + "个桃子");
}
}
猴子第一天摘了1534个桃子
39、求100之内的素数
题目:求100之内的素数
java代码1:
// 使用除sqrt(n)的方法求出的素数不包括2和3
public class Practise {
public static void main(String[] args) {
boolean b = false;
System.out.print(2 + " ");
System.out.print(3 + " ");
for (int i = 3; i < 100; i += 2) {
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
b = false;
break;
} else {
b = true;
}
}
if (b == true) {
System.out.print(i + " ");
}
}
}
}
java代码2:
// 该程序使用除1位素数得2位方法,运行效率高通用性差。
public class Practise {
public static void main(String[] args) {
int[] a = new int[] { 2, 3, 5, 7 };
for (int j = 0; j < 4; j++)
System.out.print(a[j] + " ");
boolean b = false;
for (int i = 11; i < 100; i += 2) {
for (int j = 0; j < 4; j++) {
if (i % a[j] == 0) {
b = false;
break;
} else {
b = true;
}
}
if (b == true) {
System.out.print(i + " ");
}
}
}
}
40、将一个数组逆序输出
import java.util.Scanner;
public class Practise {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int a[] = new int[20];
System.out.println("请输入多个正整数(输入-1表示结束):");
int i = 0, j;
do {
a[i] = s.nextInt();
i++;
} while (a[i - 1] != -1);
System.out.println("你输入的数组为:");
for (j = 0; j < i - 1; j++) {
System.out.print(a[j] + " ");
}
System.out.println("\n数组逆序输出为:");
for (j = i - 2; j >= 0; j = j - 1) {
System.out.print(a[j] + " ");
}
}
}
运行结果如下:
你输入的数组为:
99 11 88 22 77 33
数组逆序输出为:
33 77 22 88 11 99
六、JVM调优
1、java内存模型
从JDK5开始,Java使用新的内存模型,新内存模型完全抛弃了旧内存模型的主内存和工作内存的概念,也抛弃了旧内存模型的8个内存操作。也就是说,新内存模型完全是重新设计的。
新内存模型引入了一个新的概念,叫happens-before。happens-before的概念最初由LeslieLamport在其一篇影响深远的论文(《Time,ClocksandtheOrderingofEventsinaDistributedSystem》)中提出。LeslieLamport使用happens-before来定义分布式系统中,事件之间的一个偏序关系(partialordering)。LeslieLamport在这篇论文中给出了一个分布式算法,该算法可以将该偏序关系扩展为某种全序关系。
JSR-133使用happens-before的概念来指定两个操作(这里的操作是指程序中对变量的读/写,对锁的加锁和解锁)之间的执行顺序。新内存模型定义了如下的happens-before规则。
• 程序顺序规则:一个线程中的每个操作,happensbefore于该线程中的任意后续操作。
• 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
• volatile变量规则:对一个volatile域的写,happensbefore于任意后续对这个volatile域的读。
• 传递性:如果Ahappens-beforeB,且BhappensbeforeC,那么Ahappens-beforeC。
• start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happensbefore于线程B中的任意操作。
• join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
由于两个操作可以在一个线程之内,也可以是在不同线程之间。因此JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM
图2新内存模型的设计示意图
向程序员保证a操作将对b操作可见)。在新内存模型向程序员提供happens-before规则,程序员只需要与happens-before打交道即可,因此Java程序员的学习负担大大降低。同时,新内存模型允许不会改变程序结果的重排序,这可以最大限度地放松对编译器和处理器的束缚,新内存模型的执行性能比旧内存模型要好。
2、GC概念
gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。
在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和jvm支持的垃圾收集算法,便可以进行优化配置垃圾收集器。
垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集。两种常用的方法是引用计数和对象引用遍历。
引用计数
引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时,jvm必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。
对象引用遍历
早期的jvm使用引用计数,现在大多数jvm采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。
下一步,gc要删除不可到达的对象。删除时,有些gc只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。因此,许多gc可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。
为此,gc需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止,只有gc运行。结果,在响应期间增减了许多混杂请求。另外,更复杂的gc不断增加或同时运行以减少或者清除应用程序的中断。有的gc使用单线程完成这项工作,有的则采用多线程以增加效率。
3、几种垃圾回收机制
- 标记-清除收集器
这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。 - 标记-压缩收集器
有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。 - 复制收集器
这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,jvm生成的新对象则放在另一半空间中。gc运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。 - 增量收集器
增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。 - 分代收集器
这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。jvm生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。 - 并发收集器
并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。 - 并行收集器
并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多cpu机器上使用多线程技术可以显著的提高java应用程序的可扩展性。
4、JVM堆大小的调整
SunHotSpot1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Jvm生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。
下面介绍如何控制这些域的大小。可使用-Xms和-Xmx控制整个堆的原始大小或最大值。
下面的命令是把初始大小设置为128M:
java–Xms128m
–Xmx256m为控制新域的大小,可使用-XX:NewRatio设置新域在堆中所占的比例。
下面的命令把整个堆设置成128m,新域比率设置成3,即新域与旧域比例为1:3,新域为堆的1/4或32M:
java–Xms128m–Xmx128m
–XX:NewRatio=3可使用-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值。
下面的命令把新域的初始值和最大值设置成64m:
java–Xms256m–Xmx256m–Xmn64m
永久域默认大小为4m。运行程序时,jvm会调整永久域的大小以满足需要。每次调整时,jvm会对堆进行一次完全的垃圾收集。
使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogicServer应用程序加载较多类时,经常需要增加永久域的最大值。当jvm加载类时,永久域中的对象急剧增加,从而使jvm不断调整永久域大小。为了避免调整,可使用-XX:PerSize标志设置初始值。
下面把永久域初始值设置成32m,最大值设置成64m。
java-Xms512m-Xmx512m-Xmn128m-XX:PermSize=32m-XX:MaxPermSize=64m
默认状态下,HotSpot在新域中使用复制收集器。该域一般分为三个部分。第一部分为Eden,用于生成新的对象。另两部分称为救助空间,当Eden充满时,收集器停止应用程序,把所有可到达对象复制到当前的from救助空间,一旦当前的from救助空间充满,收集器则把可到达对象复制到当前的to救助空间。From和to救助空间互换角色。维持活动的对象将在救助空间不断复制,直到它们获得使用期并转入旧域。使用-XX:SurvivorRatio可控制新域子空间的大小。
同NewRation一样,SurvivorRation规定某救助域与Eden空间的比值。比如,以下命令把新域设置成64m,Eden占32m,每个救助域各占16m:
java-Xms256m-Xmx256m-Xmn64m-XX:SurvivorRation=2
如前所述,默认状态下HotSpot对新域使用复制收集器,对旧域使用标记-清除-压缩收集器。在新域中使用复制收集器有很多意义,因为应用程序生成的大部分对象是短寿命的。理想状态下,所有过渡对象在移出Eden空间时将被收集。如果能够这样的话,并且移出Eden空间的对象是长寿命的,那么理论上可以立即把它们移进旧域,避免在救助空间反复复制。但是,应用程序不能适合这种理想状态,因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中,因为复制小部分的对象总比压缩旧域廉价。为控制新域中对象的复制,可用-XX:TargetSurvivorRatio控制救助空间的比例(该值是设置救助空间的使用比例。如救助空间位1M,该值50表示可用500K)。该值是一个百分比,默认值是50。当较大的堆栈使用较低的sruvivorratio时,应增加该值到80至90,以更好利用救助空间。用-XX:maxtenuringthreshold可控制上限。
为放置所有的复制全部发生以及希望对象从eden扩展到旧域,可以把MaxTenuringThreshold设置成0。设置完成后,实际上就不再使用救助空间了,因此应把SurvivorRatio设成最大值以最大化Eden空间,设置如下:
java…-XX:MaxTenuringThreshold=0–XX:SurvivorRatio=50000…
5、BEA JRockit JVM的使用
BeaWebLogic8.1使用的新的JVM用于Intel平台。在Bea安装完毕的目录下可以看到有一个类似于jrockit81sp1_141_03的文件夹。这就是Bea新JVM所在目录。不同于HotSpot把Java字节码编译成本地码,它预先编译成类。JRockit还提供了更细致的功能用以观察JVM的运行状态,主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及memory参数)或者WebLogicServer控制台。
BeaJRockitJVM支持4种垃圾收集器:
分代复制收集器
它与默认的分代收集器工作策略类似。对象在新域中分配,即JRockit文档中的nursery。这种收集器最适合单cpu机上小型堆操作。
单空间并发收集器
该收集器使用完整堆,并与背景线程共同工作。尽管这种收集器可以消除中断,但是收集器需花费较长的时间寻找死对象,而且处理应用程序时收集器经常运行。如果处理器不能应付应用程序产生的垃圾,它会中断应用程序并关闭收集。
分代并发收集器这种收集器在护理域使用排它复制收集器,在旧域中则使用并发收集器。由于它比单空间共同发生收集器中断频繁,因此它需要较少的内存,应用程序的运行效率也较高,注意,过小的护理域可以导致大量的临时对象被扩展到旧域中。这会造成收集器超负荷运作,甚至采用排它性工作方式完成收集。
并行收集器
该收集器也停止其他进程的工作,但使用多线程以加速收集进程。尽管它比其他的收集器易于引起长时间的中断,但一般能更好的利用内存,程序效率也较高。
默认状态下,JRockit使用分代并发收集器。要改变收集器,可使用-Xgc:,对应四个收集器分别为gencopy,singlecon,gencon以及parallel。可使用-Xms和-Xmx设置堆的初始大小和最大值。要设置护理域,则使用-Xns:java–jrockit–Xms512m–Xmx512m–Xgc:gencon–Xns128m…尽管JRockit支持-verbose:gc开关,但它输出的信息会因收集器的不同而异。JRockit还支持memory、load和codegen的输出。
注意:如果使用JRockitJVM的话还可以使用WLS自带的console(C:\bea\jrockit81sp1_141_03\bin下)来监控一些数据,如cpu,memery等。要想能构监控必须在启动服务时startWeblogic.cmd中加入-Xmanagement参数。
6、如何从JVM中获取信息来进行调整?
-verbose.gc开关可显示gc的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。打开-xx:+printgcdetails开关,可以详细了解gc中的变化。打开-XX:+PrintGCTimeStamps开关,可以了解这些垃圾收集发生的时间,自jvm启动以后以秒计量。最后,通过-xx:+PrintHeapAtGC开关了解堆的更详细的信息。为了了解新域的情况,可以通过-XX:=PrintTenuringDistribution开关了解获得使用期的对象权。
7、Pdm系统JVM调整
服务器:前提内存1G单CPU
可通过如下参数进行调整:-server启用服务器模式(如果CPU多,服务器机建议使用此项)
-Xms,-Xmx一般设为同样大小。800m
-Xmn是将NewSize与MaxNewSize设为一致。320m
-XX:PerSize64m
-XX:NewSize320m此值设大可调大新对象区,减少FullGC次数
-XX:MaxNewSize320m
-XX:NewRatoNewSize设了可不设。
-XX:SurvivorRatio
-XX:userParNewGC可用来设置并行收集
-XX:ParallelGCThreads可用来增加并行度
-XXUseParallelGC设置后可以使用并行清除收集器
-XX:UseAdaptiveSizePolicy与上面一个联合使用效果更好,利用它可以自动优化新域大小以及救助空间比值
客户机:通过在JNLP文件中设置参数来调整客户端JVM
JNLP中参数:initial-heap-size和max-heap-size
这可以在framework的RequestManager中生成JNLP文件时加入上述参数,但是这些值是要求根据客户机的硬件状态变化的(如客户机的内存大小等)。建议这两个参数值设为客户机可用内存的60%(有待测试)。为了在动态生成JNLP时以上两个参数值能够随客户机不同而不同,可靠虑获得客户机系统信息并将这些嵌到首页index.jsp中作为连接请求的参数。
在设置了上述参数后可以通过Visualgc来观察垃圾回收的一些参数状态,再做相应的调整来改善性能。一般的标准是减少fullgc的次数,最好硬件支持使用并行垃圾回收(要求多CPU)。
8、加速垃圾回收方式?
1.垃圾回收
对象是使用new创建的,但是并没有与之相对应的delete操作来回收对象占用的内存.当我们完成对某个对象的使用时,只需停止该对象的引用:
->将引用改变为指向其他对象
->将引用指向null
->从方法中返回,使得该方法的局部变量不复存在
要点:
->当我们从任何可执行代码都无法到达某个对象时,它所占用的空间就可以被回收.
->垃圾回收意味着我们永远不用担心出现虚悬引用(danglingreference).虚悬引用,指得是引用已经被删除的内存空间.在那些程序员可以直接控制何时删除对象的系统中,会存在这样的问题.
->垃圾回收器模型:引用计数器法(不能解决循环引用),标记-清除(mark-and-sweep)
2.终结
finalize方法
->在垃圾回收器确定该对象是不可达的且该对象的空间将被回收之后,垃圾回收器就会调用这个方法.
->这个方法可以清除该对象所使用的所有非内存资源,对每一个对象最多只能调用一次,即使在这个方法的执行使得该对象重新变为可达之后又马上会再次变为不可达的情况下,该方法也只能调用一次.
->finalize方法可以在任何特定的时间段内被调用,它也可能永远不会被调用(java虚拟机结束).
覆写finalize方法
->当一个对象变成垃圾时,它所引用的其他对象也很有可能会变成垃圾.这些垃圾可能在调用我们编写的finalize方法之前就已经被终结了,因此它们可能处于不可预知的状态.
->覆写finalize方法是,加上super.finalize方法.最好加在finally字句里面.保证其超类中声明的部分内容也可以被终结.
3.与垃圾回收器交互的相关类和方法
类:Runtime.getRuntime(),System
方法:gc(),runFinalization(),freeMemory(),totalMemory(),maxMemory()
System类支持静态的gc()和runFinalization()方法,它们将调用当前Runtime对象上的相应方法.
4.可达性状态和引用对象
对象只有在没有任何引用指定它的时候才可以被当作垃圾回收,但有时我们可能希望在仍旧有选定引用指向对象时,将该对象作为垃圾回收掉.
引用对象的唯一用途就是维护对另一个被称为指称物(referent)的对象的引用.通常我们通过字段或者局部变量来维护对对象的引用,但是现在我们可以维护对引用对象的直接引用,而该引用对象包装了我们实际需要的对象.垃圾回收器可能判断出对某个对象的残留引用是否都是经由引用对象面引用到该对象的,因此它可以决定是否要回收该对象.引用对象的强度将决定垃圾回收器的行为,普通的引用都是强度最大的引用.
Reference类
->包:java.lang.ref
->典型方法:get(),clear(),enqueue(),isEnqueued()
引用和可达性强度
->对象是强可达的(stronglyreachable):普通的引用
->对象是软可达的(softlyreachable):SoftReference
->对象是弱可达的(weaklyreachable):WeakReference
->对象是虚可达的(phantomreachable):PhantomReference
->对象是不可达的:没有引用链接
一旦对象变为弱可达的(或者列弱),它就可以被终结.如果在终结之后该对象是不可达的,那么它就可以被回收了.
对象可达性阶段会触发垃圾回收器对相关的引用对象类型做出适当的行为:
->软可达对象可能会任凭垃圾回收器去回收.我们可确定的是所有对软可达对象的SoftReference都会在抛出outofMemoryError错误这前被清除.
->弱可达对象将会被垃圾回收器回收.
->虚可达对象并不是真正意义上的可达,因为无法通过PhantomReference访问其指称对象,其get方法总是返回null.但是虚引用的存在可以防止对象在显式清除虚引用之前被回收.虚引用使我们可以处理那些finalize方法已经被调用过的对象,从而可以安全地认为它们是"死"的.
9、JVM的生命周期
当启动一个Java程序时,一个JVM实例就产生了,任何一个拥有publicstaticvoidmain(String[]args)函数的class都可以作为JVM实例运行的起点,既然如此,那么JVM如何知道是运行classA的main而不是运行classB的main呢?这就需要显式的告诉JVM类名,也就是我们平时运行java程序命令的由来,如javaclassAhelloworld,这里java是告诉os运行Sunjava2SDK的java虚拟机,而classA则指出了运行JVM所需要的类名。
JVM实例的运行:main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程。
JVM实例的消亡:当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。
10、JVM的体系结构
新生代:可通过参数-Xmn进行设置
堆内存:可通过参数-Xms、-Xmx进行设置
持久代:可通过参数-XX:PermSize、-XX:MaxPermSize进行设置
七、Java优化
1、尽量指定类的final修饰符
带有final修饰符的类是不可派生的
如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。
2、尽量重用对象
特别是String对象的使用中,出现字符串连接情况时应用StringBuffer代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。
3、尽量使用局部变量
调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。 其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。
4、不要重复初始化变量
默认情况下,调用类的构造函数时,Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。
5、操作中需要及时释放资源
Java编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,即使关闭以释放资源。 因为对这些大对象的操作会造成系统大的开销,稍有不慎,会导致严重的后果。
6、保证过期对象的及时回收
由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露,因此,保证过期对象的及时回收具有重要意义。
JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null。
7、在JAVA+ORACLE的应用系统开发中
java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。
8、在使用同步机制时,应尽量使用方法同步代替代码块同步。
9、尽量减少对变量的重复计算
10、尽量采用lazyloading的策略,即在需要的时候才开始创建
11、慎用异常
异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。
12、不要在循环中使用: Try{ }catch(){ } 应把其放置在最外层.
13、StringBuffer的使用
StringBuffer表示了可变的、可写的字符串。
有三个构造方法:
StringBuffer();//默认分配16个字符的空间
StringBuffer(intsize);//分配size个字符的空间
StringBuffer(Stringstr);//分配16个字符+str.length()个字符空间
你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。
这里提到的构造函数是StringBuffer(intlength),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(intminimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为,然后再找出一条更好的提升性能的途径。
StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)
。如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加到34(2*16+2)
,当追加到34个字符的时候就会将容量增加到70(2*34+2)
。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍――这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增益。StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。
14、在JSP页面中关闭无用的会话
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)
这样的语句时才被创建,注意如果JSP没有显示的使用<%@ page session="false" %>
关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句
HttpSession session=HttpServletRequest.getSession(true);
这也是JSP中隐含的session对象的来历。由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
对于那些无需跟踪会话状态的页面,关闭自动创建的会话可以节省一些资源。
使用如下page指令:<%@ page session="false" %>
15、当复制大量数据时,使用System.arraycopy()命令。
16、代码重构:增强代码的可读性
17、不用new关键词创建类的实例
用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。
在使用设计模式(DesignPattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:
public static Credit getNewCredit(){
return new Credit();
}
改进后的代码使用clone()方法,如下所示:
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit)BaseCredit.clone();
}
上面的思路对于数组处理同样很有用。
18、乘法和除法,用移位操作替代乘法操作可以极大地提高性能。
19、JDBC与I/O
如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。
20、Servlet与内存使用
把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。
解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。
另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计。
21、使用缓冲标记
一些应用服务器加入了面向JSP的缓冲标记功能。例如,BEA的WebLogicServer从6.0版本开始支持这个功能,OpenSymphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断,也能够缓冲整个页面。当JSP页面执行时,如果目标片断已经在缓冲之中,则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求,并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说,这个功能极其有用。对于这类应用,页面级缓冲能够保存页面执行的结果,供后继请求使用。
22、选择合适的引用机制
在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。
include指令:例如<%@include file="copyright.html" %>
。该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。
include动作:例如<jsp:include page="copyright.jsp" />
。该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。
23、及时清除不再需要的会话
为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多会话时,如果内存容量不足,操作系统会把部分内存数据转移到磁盘,应用服务器也可能根据“最近最频繁使用”(MostRecentlyUsed)算法把部分不活跃的会话转储到磁盘,甚至可能抛出“内存不足”异常。在大规模系统中,串行化会话的代价是很昂贵的。当会话不再需要时,应当及时调用HttpSession.invalidate()
方法清除会话。HttpSession.invalidate()
方法通常可以在应用的退出页面调用.
24、HashMap的遍历效率
对HashMap中的key和value值对的遍历操作,有如下两种方法:
Map<String, String[]> paraMap = new HashMap<String, String[]>();
// 第一个循环
Set<String> appFieldDefIds = paraMap.keySet();
for (String appFieldDefId : appFieldDefIds) {
String[] values = paraMap.get(appFieldDefId);
// ......
}
// 第二个循环
for (Entry<String, String[]> entry : paraMap.entrySet()) {
String appFieldDefId = entry.getKey();
String[] values = entry.getValue();
// ......
}
第一种实现明显的效率不如第二种实现。
25、不要将数组声明为:public static final
26、Array(数组)和ArryList的使用
array([]):最高效;但是其容量固定且无法动态改变;
ArrayList:容量可动态增长;但牺牲效率;
基于效率和类型检验,应尽可能使用array,无法确定数组大小时才使用ArrayList!
ArrayList是Array的复杂版本
ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。
ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。
注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。
从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了。
27、尽量使用HashMap和ArrayList
除非必要,否则不推荐使用HashTable
和Vector
,后者由于使用同步机制,而导致了性能的开销。
28、优化GC的正确时机?
GC运行随着代码级的优化和工作负载而发生变化。因此在一个已实施性能优化的接近完成的代码库上调整GC非常重要。但是在端到端的基本原型上进行初步分析也很有必要,该原型系统使用存根代码并模拟了可代表产品环境的工作负载。这样可以捕捉该架构延迟和吞吐量的真实边界,进而决定是否纵向或横向扩展。
在下一代动态信息数据平台的原型阶段,几乎实现了所有端到端的功能,并且模拟了当前产品基础架构所服务的查询负载。从中我们获得了多种用来衡量应用性能的工作负载特征和足够长时间运行情况下的GC特征。
八、Redis相关
1、Redis简单介绍
介绍:Redis是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API的非关系型数据库。
传统数据库遵循ACID规则。而Nosql(NotOnlySQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称)一般为分布式而分布式一般遵循CAP定理。
2、Redis支持的数据类型
-
字符串(String)
- 字符串是最基本的数据类型。
- 常用命令:
SET key value
:设置键值对。GET key
:获取键对应的值。INCR key
:将键对应的值加 1。DECR key
:将键对应的值减 1。APPEND key value
:在键对应的值后面追加内容。
-
哈希(Hash)
- 哈希是键值对的集合,适合存储对象。
- 常用命令:
HSET key field value
:设置哈希表中字段的值。HGET key field
:获取哈希表中指定字段的值。HGETALL key
:获取哈希表中所有字段和值。HDEL key field [field ...]
:删除哈希表中的一个或多个字段。
-
列表(List)
- 列表是一个简单的字符串列表,按照插入顺序排序。
- 常用命令:
LPUSH key value
:在列表左侧插入元素。RPUSH key value
:在列表右侧插入元素。LPOP key
:移除并获取列表的第一个元素。RPOP key
:移除并获取列表的最后一个元素。LRANGE key start stop
:获取列表中指定范围内的元素。
-
集合(Set)
- 集合是不重复且无序的字符串集合。
- 常用命令:
SADD key member
:向集合添加成员。SMEMBERS key
:获取集合中的所有成员。SISMEMBER key member
:判断成员是否存在于集合中。SREM key member
:从集合中移除成员。
-
有序集合(Sorted Set)
- 有序集合与集合类似,但每个成员都关联了一个分数,用于排序。
- 常用命令:
ZADD key score member
:向有序集合添加元素。ZRANGE key start stop WITHSCORES
:获取有序集合中指定范围内的元素及其分数。ZREVRANGE key start stop WITHSCORES
:获取有序集合中指定范围内的元素及其分数,按降序排列。ZREM key member
:从有序集合中移除成员。
3、什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis提供了两种持久化方式:RDB(默认)和AOF
-
RDB:
rdb是RedisDataBase缩写
功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数 -
AOF:
Aof是Append-onlyfile缩写
每当执行服务器(定时)任务或者函数时flushAppendOnlyFile函数都会被调用,这个函数执行以下两个工作
aof写入保存:
WRITE:根据条件,将aof_buf中的缓存写入到AOF文件
SAVE:根据条件,调用fsync或fdatasync函数,将AOF文件保存到磁盘中。
存储结构:
内容是redis通讯协议(RESP)格式的命令文本存储。
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果两个都配了优先加载AOF
4、redis通讯协议(RESP),能解释下什么是RESP?有什么特点?
RESP是redis客户端和服务端之前使用的一种通讯协议;
RESP的特点:实现简单、快速解析、可读性好
ForSimpleStringsthefirstbyteofthereplyis"+"回复
ForErrorsthefirstbyteofthereplyis"-"错误
ForIntegersthefirstbyteofthereplyis":"整数
ForBulkStringsthefirstbyteofthereplyis"$"字符串
ForArraysthefirstbyteofthereplyis"*"数组
5、Redis有哪些架构模式?讲讲各自的特点
Redis 提供了几种常见的架构模式来满足不同应用场景的需求。以下是几种典型的 Redis 架构模式及其特点:
-
单机模式
- 特点:1、简单。
- 简单易用,适合小型应用或测试环境。
- 数据存储在单个节点上,性能受限于单台机器的资源。
- 不具备高可用性和容灾能力,一旦该节点故障,服务将不可用。
- 问题:
- 内存容量有限。
- 处理能力有限。
- 无法高可用。
- 特点:1、简单。
-
主从复制模式
Redis的复制(replication)功能允许用户根据一个Redis服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步给从服务器,从而一直保证主从服务器的数据相同。- 特点:1、master/slave角色。2、master/slave数据相同。3、降低master读压力在转交从库。
- 一个主节点(Master)负责处理写操作,多个从节点(Slave)负责读操作。
- 主节点的数据会被复制到从节点,实现数据的备份。
- 提高读取性能,减轻主节点的压力。
- 可以通过手动切换主节点来实现一定程度的高可用性,但自动故障转移需要额外配置。
- 问题:
- 无法保证高可用。
- 没有解决master写的压力。
- 特点:1、master/slave角色。2、master/slave数据相同。3、降低master读压力在转交从库。
-
哨兵模式(Sentinel)
Redis sentinel是一个分布式系统中监控redis主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:
监控(Monitoring):Sentinel会不断地检查你的主服务器和从服务器是否运作正常。
提醒(Notification):当被监控的某个Redis服务器出现问题时,Sentinel可以通过API向管理员或者其他应用程序发送通知。
自动故障迁移(Automaticfailover):当一个主服务器不能正常工作时,Sentinel会开始一次自动故障迁移操作。- 特点:1、保证高可用。2、监控各个节点。3、自动故障迁移。
- 哨兵模式主要用于监控主从复制模式中的主节点和从节点的状态。
- 当主节点发生故障时,哨兵会自动进行故障转移,选择一个新的主节点并重新配置从节点。
- 实现了自动化的高可用性,提高了系统的可靠性。
- 需要配置多个哨兵实例来提高容错能力。
- 问题:
- 主从模式,切换需要时间丢数据
- 没有解决master写的压力
- 特点:1、保证高可用。2、监控各个节点。3、自动故障迁移。
-
集群(proxy型)
Twemproxy是一个Twitter开源的一个redis和memcache快速/轻量级代理服务器;Twemproxy是一个快速的单线程代理程序,支持MemcachedASCII协议和redis协议。- 特点:
- 多种hash算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins。
- 支持失败节点自动删除。
- 后端Sharding分片逻辑对业务透明,业务方的读写方式和操作单个Redis一致。
- 问题:
- 增加了新的proxy,需要维护其高可用。
- failover逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预.。
- 特点:
-
集群(直连型)
从redis3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。- 特点:
- 无中心架构(不存在哪个节点影响性能瓶颈),少了proxy层。
- 数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布。
- 可扩展性,可线性扩展到1000个节点,节点可动态添加或删除。
- 高可用性,部分节点不可用时,集群仍可用。通过增加Slave做备份数据副本。
- 实现故障自动failover,节点之间通过gossip协议交换状态信息,用投票机制完成Slave到Master的角色提升。
- 问题:
- 资源隔离性较差,容易出现相互影响的情况。
- 数据通过异步复制,不保证数据的强一致性。
- 特点:
每种模式都有其适用的场景和局限性,选择合适的架构模式对于确保 Redis 的性能、可靠性和可扩展性至关重要。
6、什么是一致性哈希算法?什么是哈希槽?
一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hotspot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。
但现在一致性hash算法在分布式系统中也得到了广泛应用,研究过memcached缓存数据库的人都知道,memcached服务器端本身不提供分布式cache的一致性,而是由客户端来提供,具体在计算一致性hash时采用如下步骤:
首先求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。
然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。
从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但ConsistentHashing中,只有在园(continuum)上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响,如下图所示:
从redis3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
其结构特点:
1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
3、客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster负责维护node<->slot<->value。
5、Redis集群预分好16384个桶,当需要在Redis集群中放置一个key-value时,根据CRC16(key)mod16384的值,决定将一个key放到哪个桶中。
7、Redis常用命令?
https://www.runoob.com/redis/redis-keys.html
- Keys pattern
*表示区配所有
以bit开头的
查看Existskey是否存在 - Set
设置key对应的值为string类型的value。 - setnx
设置key对应的值为string类型的value。如果key已经存在,返回0,nx是notexist的意思。
删除某个key
第一次返回1删除了第二次返回0 - Expire 设置过期时间(单位秒)
- TTL 查看剩下多少时间
返回负数则key失效,key不存在了 - Setex
设置key对应的值为string类型的value,并指定此键值对应的有效期。 - Mset
一次设置多个key的值,成功返回ok表示所有的值都设置了,失败返回0表示没有任何值被设置。 - Getset
设置key的值,并返回key的旧值。 - Mget
一次获取多个key的值,如果对应key不存在,则对应返回nil。 - Incr
对key的值做加加操作,并返回新的值。注意incr一个不是int的value会返回错误,incr一个不存在的key,则设置key为1 - incrby
同incr类似,加指定值,key不存在时候会设置key,并认为原来的value是0 - Decr
对key的值做的是减减操作,decr一个不存在key,则设置key为-1 - Decrby
同decr,减指定值。 - Append
给指定key的字符串值追加value,返回新字符串值的长度。 - Strlen
取指定key的value值的长度。 - persist xxx(取消过期时间)
选择数据库(0-15库) - Select 0//选择数据库
- move age 1//把age移动到1库
- Random key 随机返回一个key
- Rename 重命名
- Type 返回数据类型
8、使用过Redis分布式锁么,它是怎么实现的?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!
9、使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
缺点:
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
能不能生产一次消费多次呢?
使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
10、什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?
-
缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。 -
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
11、redis的事务处理
1、MULTI用来组装一个事务
2、EXEC用来执行一个事务
3、DISCARD用来取消一个事物
4、WATCH用来监视一些key,一点这些key在事务执行之前被改变,则取消事务的执行
12、使用redis有哪些好处?
1、速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
2、支持丰富数据类型,支持string,list,set,sortedset,hash
3、支持事务,操作都是原子性,所谓原子性就是对数据的更改要么全部执行,要么全部不执行
4、丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
13、redis相比memcached有哪些优势?
1、memcached所有的值均是简单的字符串,redis作为其其替代者,支持更为丰富的数据类型
2、redis的速度比memcached快很多
3、redis可以持久化其数据
14、redis常见性能问题和解决方案:
1、Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
2、如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
3、为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
4、尽量避免在压力过大的主库上增加从库
5、主从复制不要用图状结构,用单向链表结构更为稳定,这样的结构方便解决单点故障问题,
实现slave对master的替换。如果master挂了,可以立刻启动slave做master,其他不变
15、redis怎么和spring进行集成
1、引入jar包
2、配置bean 在application.xml加入如下配置 → jedis配置 → reids服务器中心 → cache配置 → 不需要加入缓存的类 → 不需要缓存的方法设置缓存失效时间
16、redis内存数据集大小上升到一定大小的时候,就会执行数据淘汰策略。redis提供6种数据淘汰策略:
1、volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最新最少使用的数据淘汰
2、volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
3、volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
4、allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
5、allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
6、no-enviction(驱逐):禁止驱逐数据
17、redis常见的性能问题都有哪些?如何解决?
1、Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
2、MasterAOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要日用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
3、Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
4、Reids主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
18、分布式缓存
硬盘上的数据,缓存在别的计算机(不是程序运行的计算机)的内存上
而且可以缓存的计算机的个数不止一个,可以使n个
用户通过访问http服务器,然后访问应用服务器资源,应用服务器调用后端的数据库,
在第一次访问的时候,直接访问数据库,然后将要缓存的内容放入到memcached集群,集群
规模根据缓存文件的大小而定。在第二次访问的时候就直接进入缓存读取,不需要进行
数据库的操作。这个适合数据变化不频繁的场景,比如:互联网站显示的榜单、阅读排行等。
19、redis和memcached的区别
如果简单地比较redis和memcached的区别,大多数都会得到以下观点:
1、redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
2、redis支持数据的备份,即master-slave模式的数据备份
3、redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
在redis中,并不是所有的数据都一直存储在内存中的。这是和memcached相比一个最大的区别。
九、Nginx相关
1、请解释一下什么是Nginx?
Nginx(enginex)是一个高性能的HTTP和反向代理服务,也是一个IMAP/POP3/SMTP服务。
Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx1.0.4发布。
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
2、Nginx和apache的优缺点
- nginx相对于apache的优点:
- 轻量级,同样起web 服务,比apache 占用更少的内存及资源
- 抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx
- 能保持低资源低消耗高性能
- 高度模块化的设计,编写模块相对简单
- 社区活跃,各种高性能模块出品迅速
- apache 相对于nginx 的优点:
- rewrite ,比nginx 的rewrite 强大
- 模块超多,基本想到的都可以找到
- 少bug ,nginx 的bug 相对较多
- Nginx 配置简洁, Apache 复杂
- 最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;
nginx是异步的,多个连接(万级别)可以对应一个进程
3、请解释Nginx服务器上的Master和Worker进程分别是什么?
Master进程:读取及评估配置和维持
Worker进程:处理请求
4、请解释Nginx如何处理HTTP请求。
Nginx使用反应器模式。主事件循环等待操作系统发出准备事件的信号,这样数据就可以从套接字读取,在该实例中读取到缓冲区并进行处理。单个线程可以提供数万个并发连接。
5、Nginx反向代理为什么能够提升服务器性能?
对于后端是动态服务来说,比如Java和PHP。这类服务器(如JBoss和PHP-FPM)的IO处理能力往往不高。
Nginx有个好处是它会把Request在读取完整之前buffer住,这样交给后端的就是一个完整的HTTP请求,从而提高后端的效率,而不是断断续续的传递(互联网上连接速度一般比较慢)。同样,Nginx也可以把response给buffer住,同样也是减轻后端的压力。
6、Nginx多进程模型是如何实现高并发的?
进程数与并发数不存在很直接的关系。这取决取server采用的工作方式。如果一个server采用一个进程负责一个request的方式,那么进程数就是并发数。那么显而易见的,就是会有很多进程在等待中。等什么?最多的应该是等待网络传输。
Nginx的异步非阻塞工作方式正是利用了这点等待的时间。在需要等待的时候,这些进程就空闲出来待命了。因此表现为少数几个进程就解决了大量的并发问题。apache是如何利用的呢,简单来说:同样的4个进程,如果采用一个进程负责一个request的方式,那么,同时进来4个request之后,每个进程就负责其中一个,直至会话关闭。期间,如果有第5个request进来了。就无法及时反应了,因为4个进程都没干完活呢,因此,一般有个调度进程,每当新进来了一个request,就新开个进程来处理。nginx不这样,每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker不会这么傻等着,他会在发送完请求后,注册一个事件:“如果upstream返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有request进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。
由于webserver的工作性质决定了每个request的大部份生命都是在网络传输中,实际上花费在server机器上的时间片不多。这是几个进程就解决高并发的秘密所在。webserver刚好属于网络io密集型应用,不算是计算密集型。异步,非阻塞,使用epoll,和大量细节处的优化。也正是nginx之所以然的技术基石。
7、nginx负载均衡的4 种方式分配
nginx的upstream目前支持4种方式的分配:
1)、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
2)、weight 权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
2)、ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
3)、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
4)、url_hash(第三方)
nginx内置策略包含加权轮询和ip hash加权轮询算法分为先深搜索和先广搜索,那么nginx采用的是先深搜索算法,即将首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器;
8、Nginx优秀模块 模块设计
高度模块化设计,除了少量核心代码,其他一切接模块。
官方Nginx共有五大类型模块:核心模块、配置模块、事件模块、HTTP模块、mail模块。
要注意的是:nginx的模块是静态的,添加和删除模块都要对nginx进行重新编译,这一点与Apache的动态模块完全不同。
9、如何解决惊群现象?
惊群是多个子进程在同一时刻监听同一个端口引起的;
Nginx解决方法:同一个时刻只能有唯一一个worker子进程监听web端口,此时新连接事件只能唤醒唯一正在监听端口的worker子进程。
采用锁,互斥量实现!!
10、为什么要用Nginx?
优点:
- 跨平台、配置简单
- 非阻塞、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发
- 内存消耗小:开启10个nginx才占150M内存,Nginx采取了分阶段资源分配技术
- nginx处理静态文件好,耗费内存少
- 内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。
- 节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
- 稳定性高:宕机的概率非常小
- master/worker结构:一个master进程,生成一个或者多个worker进程
- 接收用户请求是异步的:浏览器将请求发送到nginx服务器,它先将用户请求全部接收下来,再一次性发送给后端web服务器,极大减轻了web服务器的压力
- 一边接收web服务器的返回数据,一边发送给浏览器客户端
- 网络依赖性比较低,只要ping通就可以负载均衡
- 可以有多台nginx服务器
- 事件驱动:通信机制采用epoll模型
11、为什么Nginx性能这么高?
得益于它的事件处理机制:
异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决
12、为什么不使用多线程?
Apache: 创建多个进程或线程,而每个进程或线程都会为其分配cpu和内存(线程要比进程小的多,所以worker支持比perfork高的并发),并发过大会榨干服务器资源。
Nginx: 采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量)(epoll),不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换。所以才使得Nginx支持更高的并发。
13、Nginx是如何处理一个请求的呢?
- 首先,nginx在启动时,会解析配置文件,得到需要监听的端口与ip地址,然后在nginx的master进程里面
先初始化好这个监控的socket,再进行listen - 然后再fork出多个子进程出来, 子进程会竞争accept新的连接。
此时,客户端就可以向nginx发起连接了。当客户端与nginx进行三次握手,与nginx建立好一个连接后此时,某一个子进程会accept成功,然后创建nginx对连接的封装,即ngx_connection_t结构体 - 接着,根据事件调用相应的事件处理模块,如http模块与客户端进行数据的交换。
- 最后,nginx或客户端来主动关掉连接,到此,一个连接就寿终正寝了
14、正向代理
一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理
正向代理总结就一句话:代理端代理的是客户端
15、反向代理
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求,发给内部网络上的服务器
并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器
反向代理总结就一句话:代理端代理的是服务端
16、动态资源、静态资源分离
动态资源、静态资源分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路
动态资源、静态资源分离简单的概括是:动态文件与静态文件的分离
17、为什么要做动、静分离?
在我们的软件开发中,有些请求是需要后台处理的(如:.jsp,.do等等),有些请求是不需要经过后台处理的(如:css、html、jpg、js等等文件)
这些不需要经过后台处理的文件称为静态文件,否则动态文件。因此我们后台处理忽略静态文件。这会有人又说那我后台忽略静态文件不就完了吗
当然这是可以的,但是这样后台的请求次数就明显增多了。在我们对资源的响应速度有要求的时候,我们应该使用这种动静分离的策略去解决
动、静分离将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问
这里我们将静态资源放到nginx中,动态资源转发到tomcat服务器中
18、负载均衡概念
负载均衡即是代理服务器将接收的请求均衡的分发到各服务器中
负载均衡主要解决网络拥塞问题,提高服务器响应速度,服务就近提供,达到更好的访问质量,减少后台服务器大并发压力
十、WebService相关
1、什么是 webService?
WebService 是一种跨编程语言和跨操作系统平台的远程调用技术。所谓跨编程语言和跨操作平台,就是说服务端程序采用 java 编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上。
2、webService相关术语
-
名词1:XML. Extensible Markup Language - 扩展性标记语言
- XML,用于传输格式化的数据,是Web服务的基础。
- namespace - 命名空间
- xmlns= “http://itcast.cn” 使用默认命名空间。
- xmlns:itcast= “http://itcast.cn” 使用指定名称的命名空间。
-
名词2: WSDL - WebService Description Language - Web服务描述语言。
- 通过XML形式说明服务在什么地方 - 地址。
- 通过XML形式说明服务提供什么样的方法 - 如何调用。
-
名词3: SOAP-Simple Object Access Protocol(简单对象访问协议)
- SOAP作为一个基于XML语言的协议用于有网上传输数据。
- SOAP = 在HTTP的基础上+XML数据。
- SOAP是基于HTTP的。
- SOAP的组成如下:
- Envelope - 必须的部分。以XML的根元素出现。
- Headers - 可选的。
- Body - 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。
3、SOAP是什么?
SOAP是simple object access protocal的缩写,即简单对象访问协议。 是基于XML和HTTP的一种通信协议。是webservice所使用的一种传输协议,webservice之所以能够做到跨语言和跨平台,主要是因为XML和HTTP都是独立于语言和平台的。Soap的消息分为请求消息和响应消息,一条SOAP消息就是一个普通的XML文档,包含下列元素:
1、必需的 Envelope 元素,可把此XML文档标识为一条SOAP消息
2、可选的 Header 元素,包含头部信息
3、必需的 Body 元素,包含所有的调用和响应信息
4、可选的 Fault 元素,提供有关在处理此消息所发生错误的信息
Soap请求消息
Soap响应消息
4、WSDL文档主要有那几部分组成,分别有什么作用?
一个WSDL文档的根元素是definitions元素,WSDL文档包含7个重要的元素:types, import, message, portType, operations, binding和service元素。
1、definitions元素中一般包括若干个XML命名空间;
2、Types元素用作一个容器,定义了自定义的特殊数据类型,在声明消息部分(有效负载)的时候,messages定义使用了types元素中定义的数据类型与元素;
3、Import元素可以让当前的文档使用其他WSDL文档中指定命名空间中的定义;
4、Message元素描述了Web服务的有效负载。相当于函数调用中的参数和返回值;
5、PortType元素定义了Web服务的抽象接口,它可以由一个或者多个operation元素,每个operation元素定义了一个RPC样式或者文档样式的Web服务方法;
6、Operation元素要用一个或者多个messages消息来定义它的输入、输出以及错误;
7、Binding元素将一个抽象的portType映射到一组具体的协议(SOAP或者HTTP)、消息传递样式(RPC或者document)以及编码样式(literal或者SOAP encoding)
8、Service元素包含一个或者多个Port元素
每一个Port元素对应一个不同的Web服务,port将一个URL赋予一个特定的binding,通过location实现。
可以使两个或者多个port元素将不同的URL赋给相同的binding。
5、怎么理解UDDI?
UDDI是Universal Description Discovery and Integration的缩写,即统一描述、发现和整合规范。用来注册和查找服务,把web services收集和存储起来,这样当别人访问这些信息的时候就从UDDI中查找,看有没有这个信息存在。
6、Webservice的SEI指什么?
WebService EndPoint Interface(webservice终端[Server端]接口)
就是 WebService服务器端用来处理请求的接口
7、说说你知道的webservice框架,他们都有什么特点?
Webservice常用框架有JWS、Axis2、XFire以及CXF。
下面分别介绍一个这几种Web Service框架的基本概念
1、JWS是Java语言对WebService服务的一种实现,用来开发和发布服务。而从服务本身的角度来看JWS服务是没有语言界限的。但是Java语言为Java开发者提供便捷发布和调用WebService服务的一种途径。
2、Axis2是Apache下的一个重量级WebService框架,准确说它是一个Web Services / SOAP / WSDL 的引擎,是WebService框架的集大成者,它能不但能制作和发布WebService,而且可以生成Java和其他语言版WebService客户端和服务端代码。这是它的优势所在。但是,这也不可避免的导致了Axis2的复杂性,使用过的开发者都知道,它所依赖的包数量和大小都是很惊人的,打包部署发布都比较麻烦,不能很好的与现有应用整合为一体。但是如果你要开发Java之外别的语言客户端,Axis2提供的丰富工具将是你不二的选择。
3、XFire是一个高性能的WebService框架,在Java6之前,它的知名度甚至超过了Apache的Axis2,XFire的优点是开发方便,与现有的Web整合很好,可以融为一体,并且开发也很方便。但是对Java之外的语言,没有提供相关的代码工具。XFire后来被Apache收购了,原因是它太优秀了,收购后,随着Java6 JWS的兴起,开源的WebService引擎已经不再被看好,渐渐的都败落了。
4、CXF是Apache旗下一个重磅的SOA简易框架,它实现了ESB(企业服务总线)。CXF来自于XFire项目,经过改造后形成的,就像目前的Struts2来自WebWork一样。可以看出XFire的命运会和WebWork的命运一样,最终会淡出人们的视线。CXF不但是一个优秀的Web Services / SOAP / WSDL 引擎,也是一个不错的ESB总线,为SOA的实施提供了一种选择方案,当然他不是最好的,它仅仅实现了SOA架构的一部分。
注:对于Axis2与CXF之间的关系,一个是Axis2出现的时间较早,而CXF的追赶速度快。
如何抉择:
1、如果应用程序需要多语言的支持,Axis2应当是首选了;
2、如果应用程序是遵循 spring哲学路线的话,Apache CXF是一种更好的选择,特别对嵌入式的Web Services来说;
3、如果应用程序没有新的特性需要的话,就仍是用原来项目所用的框架,比如 Axis1,XFire,Celtrix或BEA等等厂家自己的Web Services实现,就别劳民伤财了。
十一、其他
1、经常访问的技术网站:
- csdn(详细步骤的描述)
- iteye(详细步骤的描述)
- oschina(开源中国获取java开源方面的信息技术)
- java开源大全www.open-open.com(获取java开源方面的信息技术)
- infoq(对java,php,.net等这些语言的一些最新消息的报道)
- cnblog(博客园)
2、项目团队中交流的工具
- 飞秋(局域网)
- qq(局域网,外网)
- RTX(局域网,外网)
- 邮箱(局域网,外网)
3、平时浏览的书籍
- 思想基础:
- Java编程思想
- 大话设计模式
- 重构
- 实战经验:
- ***inaction(实战)
- ***深入浅出
- ***入门指南
……
4、OSI七层模型
- 应用层:与其他计算机进行通讯的一个应用,它是对应应用程序的通信服务的。示例:telnet,HTTP,FTP,WWW,NFS,SMTP等。
- 表示层:这一层的主要功能是定义数据格式及加密。示例:加密,ASII等。
- 会话层:他定义了如何开始、控制和结束一个会话,包括对多个双向小时的控制和管理,以便在只完成连续消息的一部分时可以通知应用,从而使表示层看到的数据是连续的。示例:RPC,SQL等。
- 传输层:这层的功能包括是否选择差错恢复协议还是无差错恢复协议,及在同一主机上对不同应用的数据流的输入进行复用,还包括对收到的顺序不对的数据包的重新排序功能。示例:TCP,UDP,SPX。
- 网络层:这层对端到端的包传输进行定义,他定义了能够标识所有结点的逻辑地址,还定义了路由实现的方式和学习的方式。。示例:IP,IPX等。
- 数据链路层:他定义了在单个链路上如何传输数据。
- 物理层:OSI的物理层规范是有关传输介质的特性标准,这些规范通常也参考了其他组织制定的标准。
5、项目的生命周期
- 需求分析
- 概要设计
- 详细设计(用例图,流程图,类图)
- 数据库设计(powerdesigner)
- 代码开发(编写)
- 单元测试(junit白盒测试)(开发人员)
svn版本管理工具(提交,更新代码,文档) - 集成测试(黑盒测试,loadrunner(编写测试脚本)(高级测试))
- 上线试运行(用户自己体验)
- 压力测试(loadrunner)
- 正式上线
- 维护
6、什么是典型的软件三层结构?软件设计为什么要分层?软件分层有什么好处?
- Presentationlayer(表示层)
- (1)表示逻辑(生成界面代码)
- (2)接收请求
- (3)处理业务层抛出的异常
- (4)负责规则验证(数据格式,数据非空等)
- Servicelayer(服务层/业务层)
- (1)封装业务逻辑处理,并且对外暴露接口
- (2)负责事务,安全等服务
- Persistencelayer(持久层)
- (1)封装数据访问的逻辑,暴露接口
- (2)提供方便的数据访问的方案(查询语言,API,映射机制等)
- Domainlayer(域层)
- (1)业务对象以及业务关系的表示
- (2)处理简单的业务逻辑
- (3)域层的对象可以穿越表示层,业务层,持久层
软件分层结构使得代码维护非常方便,设计明确,各层独立,专注自己擅长的领域。
7、一个J2EE项目团队的主要人员组成是什么?
Technicalarchitect 技术架构师
Project manager 项目经理
Business analyst 业务分析师
Layout designer UI 设计师
Presentation-tier developer Web工程师
Business logic developer 后台开发人员
Data modeler 数据建模师
Database administrator
Datamigration specialist 数据迁移专家
Infrastructure specialist
Testing specialist 测试人员
如下表所示:
项目经理 (PM) | 质量保障工程师 (QA) |
架构设计师 (AE) | |
开发经理 (TTL) | 实施工程师 (CE) |
数据库管理员 (DBA) | |
软件设计师 (DE) | 测试工程师 (TE) |
开发工程师 (SE) | |
UI设计师 (UID) | |
需求分析员 (RE) |
8、什么是UML?
UML是统一建模语言(UML是UnifiedModelingLanguage的缩写)是用来对软件密集系统进行可视化建模的一种语言。UML为面向对象开发系统的产品进行说明、可视化、和编制文档的一种标准语言。
统一建模语言(UML)是非专利的第三代建模和规约语言。UML是在开发阶段,说明,可视化,构建和书写一个面向对象软件密集系统的制品的开放方法。UML展现了一系列最佳工程实践,这些最佳实践在对大规模,复杂系统进行建模方面,特别是在软件架构层次已经被验证有效。
UML可以贯穿软件开发周期中的每一个阶段。被OMG采纳作为业界的标准。
UML最适于数据建模,业务建模,对象建模,组件建模。
UML作为一种模型语言,它使开发人员专注于建立产品的模型和结构,而不是选用什么程序语言和算法实现。当模型建立之后,模型可以被UML工具转化成指定的程序语言代码。
IBM的RationalRose和MS的Visio都是UML工具。
9、JUnit单元测试
JUnit是一套基于测试驱动开发的测试框架。
注:
JUnit4不需要继承junit.framework.TestCase类。
JUnit3中所有的方法都必须使用@Test
注解,并且需要在方法名前加test作为前缀,需要继承junit.framework.TestCase类。
10、版本管理工具SVN
SVN是一个开放源代码的版本控制系统。
举个例子:
①个人的代码→口袋里的钱
②版本控制工具中的代码→卡里的钱
③版本控制工具中的客户端→卡
④版本控制工具中的服务端→刷卡机
注:在实际版本管理当中,你所写的代码,需要提交给服务端,然后别人要修改你的代码,就会从服务端更新当前目录下的代码,再进行修改和提交。所以,“卡里的钱”可以理解为团队公有的代码体系。
11、项目管理工具maven
maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。
注:
maven也是一套强大的自动化的管理工具,覆盖了编译、测试、运行、清理、打包和部署,整个项目构建的周期。
它还提供了一个仓库的概念,统一的帮助开发人员管理项目所依赖的第三方的架包,最大可能的避免环境配置不同所产生的问题(在你的电脑上能运行,在我的电脑上就不能运行)。