目录
索引
Java 基础知识篇
Java web 基础知识总结
Java 集合篇常见问题
Java 基础知识篇
面向对象和面向过程的区别
面向过程:
优点: 性能比面向对象高, 因为类调用时需要实例化, 开销比较大, 比较消耗资源; 比如单片机, 嵌入式开发, Linux/Unix 等一般采用面向过程开发, 性能是最重要的因素.
缺点: 没有面向对象易维护, 易复用, 易扩展
面向对象:
优点: 易维护, 易复用, 易扩展, 由于面向对象有封装, 继承, 多态性的特性, 可以设计出低耦合的系统, 使系统更加灵活, 更加易于维护
缺点: 性能比面向过程低
Java 语言有哪些特点?
1, 简单易学; 2, 面向对象(封装, 继承, 多态);3, 平台无关性(Java 虚拟机实现平台无关性);4, 可靠性; 5, 安全性; 6, 支持多线程(C++ 语言没有内置的多线程机制, 因此必须调用操作系统的多线程功能来进行多线程程序设计, 而 Java 语言却提供了多线程支持);7, 支持网络编程并且很方便(Java 语言诞生本身就是为简化网络编程设计的, 因此 Java 语言不仅支持网络编程而且很方便);8, 编译与解释并存;
什么是字节码? 采用字节码的最大好处是什么? 什么 Java 是虚拟机?
先看下 java 中的编译器和解释器:
Java 中引入了虚拟机的概念, 即在机器和编译程序之间加入了一层抽象的虚拟的机器. 这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口. 编译程序只需要面向虚拟机, 生成虚拟机能够理解的代码, 然后由解释器来将虚拟机代码转换为特定系统的机器码执行. 在 Java 中, 这种供虚拟机理解的代码叫做字节码(即扩展名为. class 的文件), 它不面向任何特定的处理器, 只面向虚拟机. 每一种平台的解释器是不同的, 但是实现的虚拟机是相同的. Java 源程序经过编译器编译后变成字节码, 字节码由虚拟机解释执行, 虚拟机将每一条要执行的字节码送给解释器, 解释器将其翻译成特定机器上的机器码, 然后在特定的机器上运行, 这就是上面提到的 Java 的特点的编译与解释并存的解释.
Java 源代码 ---->编译器 ---->jvm 可执行的 Java 字节码 (即虚拟指令)---->jvm---->jvm 中解释器 -----> 机器可执行的二进制机器码 ---->程序运行.
采用字节码的好处:
Java 语言通过字节码的方式, 在一定程度上解决了传统解释型语言执行效率低的问题, 同时又保留了解释型语言可移植的特点. 所以 Java 程序运行时比较高效, 而且, 由于字节码并不专对一种特定的机器, 因此, Java 程序无须重新编译便可在多种不同的计算机上运行.
什么是 Java 虚拟机
任何一种可以运行 Java 字节码的软件均可看成是 Java 的虚拟机(JVM)
什么是 Java 程序的主类? 应用程序和小程序的主类有何不同?
一个程序中可以有多个类, 但只能有一个类是主类. 在 Java 应用程序中, 这个主类是指包含 main()方法的类. 而在 Java 小程序中, 这个主类是一个继承自系统类 JApplet 或 Applet 的子类. 应用程序的主类不一定要求是 public 类, 但小程序的主类要求必须是 public 类. 主类是 Java 程序执行的入口点.
什么是 JDK? 什么是 JRE?
JDK: 顾名思义它是给开发者提供的开发工具箱, 是给程序开发者用的. 它除了包括完整的 JRE(Java Runtime Environment),Java 运行环境, 还包含了其他供开发者使用的工具包.
JRE: 普通用户而只需要安装 JRE(Java Runtime Environment)来 来运行 Java 程序. 而程序开发者必须安装 JDK 来编译, 调试程序.
环境变量 Path 和 ClassPath 的作用是什么? 如何设置这两个环境变量?
Java 环境变量 PATH 和 CLASSPATH - 简书 http://www.jianshu.com/p/d63b099cf283
Java 应用程序与小程序之间有那些差别?
简单说应用程序是从主线程启动 (也就是 main() 方法).applet 小程序没有 main 方法, 主要是嵌在浏览器页面上运行 (调用 init() 线程或者 run()来启动), 嵌入浏览器这点跟 flash 的小游戏类似.
字符型常量和字符串常量的区别
1) 形式上:
字符常量是单引号引起的一个字符
字符串常量是双引号引起的若干个字符
2) 含义上:
字符常量相当于一个整形值(ASCII 值), 可以参加表达式运算
字符串常量代表一个地址值(该字符串在内存中存放位置)
3) 占内存大小
字符常量只占一个字节
字符串常量占若干个字节(至少一个字符结束标志)
Java 语言采用何种编码方案? 有何特点?
Java 语言采用 Unicode 编码标准, Unicode(标准码), 它为每个字符制订了一个唯一的数值, 因此在任何的语言, 平台, 程序都可以放心的使用.
构造器 Constructor 是否可被 override
在讲继承的时候我们就知道父类的私有属性和构造方法并不能被继承, 所以 Constructor 也就不能被 override, 但是可以 overload, 所以你可以看到一个类中有多个构造函数的情况.
重载和重写的区别
重载: 发生在同一个类中, 方法名必须相同, 参数类型不同, 个数不同, 顺序不同, 方法返回值和访问修饰符可以不同, 发生在编译时.
重写: 发生在父子类中, 方法名, 参数列表必须相同, 返回值小于等于父类, 抛出的异常小于等于父类, 访问修饰符大于等于父类; 如果父类方法访问修饰符为 private 则子类中就不是重写.
java 面向对象编程三大特性 ------ 封装, 继承, 多态
https://blog.csdn.net/jianyuerensheng/article/details/51602015
String 和 StringBuffer,StringBuilder 的区别是什么? String 为什么是不可变的?
可变性
String 类中使用字符数组保存字符串, private final char value[], 所以 string 对象是不可变的. StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类, 在 AbstractStringBuilder 中也是使用字符数组保存字符串, char[]value, 这两种对象都是可变的.
线程安全性
String 中的对象是不可变的, 也就可以理解为常量, 线程安全. AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类, 定义了一些字符串的基本操作, 如 expandCapacity,append,insert,indexOf 等公共方法. StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁, 所以是线程安全的. StringBuilder 并没有对方法进行加同步锁, 所以是非线程安全的.
性能
每次对 String 类型进行改变的时候, 都会生成一个新的 String 对象, 然后将指针指向新的 String 对象. StringBuffer 每次都会对 StringBuffer 对象本身进行操作, 而不是生成新的对象并改变对象引用. 相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升, 但却要冒多线程不安全的风险.
对于三者使用的总结:
如果要操作少量的数据用 = String
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
自动装箱与拆箱
装箱: 将基本类型用它们对应的引用类型包装起来;
拆箱: 将包装类型转换为基本数据类型;
Java 使用自动装箱和拆箱机制, 节省了常用数值的内存开销和创建对象的开销, 提高了效率, 由编译器来完成, 编译器会在编译期根据语法决定是否进行装箱和拆箱动作.
类, 方法, 成员变量和局部变量的可用修饰符 -
http://blog.csdn.net/yttcjj/article/details/6939239
在一个静态方法内调用一个非静态成员为什么是非法的?
由于静态方法可以不通过对象进行调用, 因此在静态方法里, 不能调用其他非静态变量, 也不可以访问非静态变量成员.
在 Java 中定义一个不做事且没有参数的构造方法的作用
Java 程序在执行子类的构造方法之前, 如果没有用 super()来调用父类特定的构造方法, 则会调用父类中 "没有参数的构造方法". 因此, 如果父类中只定义了有参数的构造方法, 而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法, 则编译时将发生错误, 因为 Java 程序在父类中找不到没有参数的构造方法可供执行. 解决办法是在父类里加上一个不做事且没有参数的构造方法.
import java 和 javax 有什么区别
http://www.cnblogs.com/EasonJim/p/6993139.html
接口和抽象类的区别是什么?
1. 接口的方法默认是 public, 所有方法在接口中不能有实现, 抽象类可以有非抽象的方法
2. 接口中的实例变量默认是 final 类型的, 而抽象类中则不一定
3. 一个类可以实现多个接口, 但最多只能实现一个抽象类
4. 一个类实现接口的话要实现接口的所有方法, 而抽象类不一定
5. 接口不能用 new 实例化, 但可以声明, 但是必须引用一个实现该接口的对象
从设计层面来说, 抽象是对类的抽象, 是一种模板设计, 接口是行为的抽象, 是一种行为的规范.
成员变量与局部变量的区别有那些?
从语法形式上, 看成员变量是属于类的, 而局部变量是在方法中定义的变量或是方法的参数; 成员变量可以被 public,private,static 等修饰符所修饰, 而局部变量不能被访问控制修饰符及 static 所修饰; 成员变量和局部变量都能被 final 所修饰;
从变量在内存中的存储方式来看, 成员变量是对象的一部分, 而对象存在于堆内存, 局部变量存在于栈内存
从变量在内存中的生存时间上看, 成员变量是对象的一部分, 它随着对象的创建而存在, 而局部变量随着方法的调用而自动消失.
成员变量如果没有被赋初值, 则会自动以类型的默认值而赋值(一种情况例外被 final 修饰但没有被 static 修饰的成员变量必须显示地赋值); 而局部变量则不会自动赋值.
创建一个对象用什么运算符? 对象实体与对象引用有何不同?
new 运算符, new 创建对象实例(对象实例在堆内存中), 对象引用指向对象实例(对象引用存放在栈内存中). 一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球, 也可以系一个气球); 一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)
什么是方法的返回值? 返回值在类的方法里的作用是什么?
方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果). 返回值的作用: 接收出结果, 使得它可以用于其他的操作!
一个类的构造方法的作用是什么? 若一个类没有声明构造方法, 改程序能正确执行吗? 为什么?
主要作用是完成对类对象的初始化工作. 可以执行. 因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法.
构造方法有哪些特性?
1, 名字与类名相同; 2, 没有返回值, 但不能用 void 声明构造函数; 3, 生成类的对象时自动执行, 无需调用.
静态方法和实例方法有何不同?
静态方法和实例方法的区别主要体现在两个方面:
在外部调用静态方法时, 可以使用 "类名. 方法名" 的方式, 也可以使用 "对象名. 方法名" 的方式. 而实例方法只有后面这种方式. 也就是说, 调用静态方法可以无需创建对象.
静态方法在访问本类的成员时, 只允许访问静态成员(即静态成员变量和静态方法), 而不允许访问实例成员变量和实例方法; 实例方法则无此限制
对象的相等与指向他们的引用相等, 两者有什么不同?
对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等.
在调用子类构造方法之前会先调用父类没有参数的构造方法, 其目的是?
帮助子类做初始化工作.
什么是多态机制? Java 语言是如何实现多态的?
http://blog.csdn.net/bornlili/article/details/55213563
equals 和 == 的区别?
通俗点讲:== 是看看左右是不是一个东西. equals 是看看左右是不是长得一样. 如何记住嘛. 如果单纯是想记住,==: 等于. equals: 相同. 两个长得一样的人, 只能说长的相同(equals), 但是不等于他们俩是一个人. 你只要记住 equals,== 就不用记了.
术语来讲的区别: 1.== 是判断两个变量或实例是不是指向同一个内存空间 equals 是判断两个变量或实例所指向的内存空间的值是不是相同
2.== 是指对内存地址进行比较 equals()是对字符串的内容进行比较 3.== 指引用是否相同 equals()指的是值是否相同
创建线程有几种不同的方式? 你喜欢哪一种? 为什么?
继承 Thread 类
实现 Runnable 接口
应用程序可以使用 Executor 框架来创建线程池
实现 Callable 接口.
我更喜欢实现 Runnable 接口这种方法, 当然这也是现在大多程序员会选用的方法. 因为一个类只能继承一个父类而可以实现多个接口. 同时, 线程池也是非常高效的, 很容易实现和使用.
简述线程, 程序, 进程的基本概念. 以及他们之间关系是什么?(参考书籍:Java 程序设计基础第五版)
线程与进程相似, 但线程是一个比进程更小的执行单位. 一个进程在其执行的过程中可以产生多个线程. 与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源, 所以系统在产生一个线程, 或是在各个线程之间作切换工作时, 负担要比进程小得多, 也正因为如此, 线程也被称为轻量级进程.
程序是含有指令和数据的文件, 被存储在磁盘或其他的数据存储设备中, 也就是说程序是静态的代码.
进程是程序的一次执行过程, 是系统运行程序的基本单位, 因此进程是动态的. 系统运行一个程序即是一个进程从创建, 运行到消亡的过程. 简单来说, 一个进程就是一个执行中的程序, 它在计算机中一个指令接着一个指令地执行着, 同时, 每个进程还占有某些系统资源如 CPU 时间, 内存空间, 文件, 文件, 输入输出设备的使用权等等. 换句话说, 当程序在执行时, 将会被操作系统载入内存中.
线程是进程划分成的更小的运行单位. 线程和进程最大的不同在于基本上各进程是独立的, 而各线程则不一定, 因为同一进程中的线程极有可能会相互影响. 从另一角度来说, 进程属于操作系统的范畴, 主要是同一段时间内, 可以同时执行一个以上的程序, 而线程则是在同一程序内几乎同时执行一个以上的程序段.
什么是多线程? 为什么程序的多线程功能是必要的?
多线程就是几乎同时执行多个线程(一个处理器在某一个时间点上永远都只能是一个线程! 即使这个处理器是多核的, 除非有多个处理器才能实现多个线程同时运行.). 几乎同时是因为实际上多线程程序中的多个线程实际上是一个线程执行一会然后其他的线程再执行, 并不是很多书籍所谓的同时执行. 这样可以带来以下的好处:
使用线程可以把占据长时间的程序中的任务放到后台去处理
用户界面可以更加吸引人, 这样比如用户点击了一个按钮去触发某些事件的处理, 可以弹出一个进度条来显示处理的进度
程序的运行速度可能加快
在一些等待的任务实现上如用户输入, 文件读写和网络收发数据等, 线程就比较有用了. 在这种情况下可以释放一些珍贵的资源如内存占用等等.
还有其他很多使用多线程的好处, 这里就不一一说明了.
多线程与多任务的差异是什么?(参考书籍:Java 程序设计基础第五版)
多任务与多线程是两个不同的概念,
多任务是针对操作系统而言的, 表示操作系统可以同时运行多个应用程序.
而多线程是针对一个进程而言的, 表示在一个进程内部可以几乎同时执行多个线程
线程有哪些基本状态? 这些状态是如何定义的?
新建(new): 新创建了一个线程对象.
可运行 (runnable): 线程对象创建后, 其他线程(比如 main 线程) 调用了该对象的 start()方法. 该状态的线程位于可运行线程池中, 等待被线程调度选中, 获 取 cpu 的使用权.
运行 (running): 可运行状态(runnable) 的线程获得了 cpu 时间片(timeslice), 执行程序代码.
阻塞 (block): 阻塞状态是指线程因为某种原因放弃了 cpu 使用权, 也即让出了 cpu timeslice, 暂时停止运行. 直到线程进入可运行(runnable) 状态, 才有 机会再次获得 cpu timeslice 转到运行 (running) 状态. 阻塞的情况分三种:
(一). 等待阻塞: 运行 (running) 的线程执行 o.wait()方法, JVM 会把该线程放 入等待队列 (waitting queue) 中.
(二). 同步阻塞: 运行 (running) 的线程在获取对象的同步锁时, 若该同步锁 被别的线程占用, 则 JVM 会把该线程放入锁池 (lock pool) 中.
(三). 其他阻塞: 运行 (running) 的线程执行 Thread.sleep(long ms)或 t.join()方法, 或者发出了 I/O 请求时, JVM 会把该线程置为阻塞状态. 当 sleep()状态超时 join()等待线程终止或者超时, 或者 I/O 处理完毕时, 线程重新转入可运行 (runnable) 状态.
死亡 (dead): 线程 run(),main() 方法执行结束, 或者因异常退出了 run()方法, 则该线程结束生命周期. 死亡的线程不可再次复生.
备注:
可以用早起坐地铁来比喻这个过程:
还没起床: sleeping
起床收拾好了, 随时可以坐地铁出发: Runnable
等地铁来: Waiting
地铁来了, 但要排队上地铁: I/O 阻塞
上了地铁, 发现暂时没座位: synchronized 阻塞
地铁上找到座位: Running
到达目的地: Dead
什么是线程的同步? 程序中为什么要实现线程的同步? 是如何实现同步的?
当一个线程对共享的数据进行操作时, 应使之成为一个 "原子操作", 即在没有完成相关操作之前, 不允许其他线程打断它, 否则, 就会破坏数据的完整性, 必然会得到错误的处理结果, 这就是线程的同步.
在多线程应用中, 考虑不同线程之间的数据同步和防止死锁. 当两个或多个线程之间同时等待对方释放资源的时候就会形成线程之间的死锁. 为了防止死锁的发生, 需要通过同步来实现线程安全.
在监视器 (Monitor) 内部, 是如何做线程同步的? 程序应该做哪种级别的同步?
在 java 虚拟机中, 每个对象 ( Object 和 class ) 通过某种逻辑关联监视器, 每个监视器和一个对象引用相关联, 为了实现监视器的互斥功能, 每个对象都关联着一把锁.
一旦方法或者代码块被 synchronized 修饰, 那么这个部分就放入了监视器的监视区域, 确保一次只能有一个线程执行该部分的代码, 线程在获取锁之前不允许执行该部分的代码
另外 java 还提供了显式监视器 ( Lock ) 和隐式监视器 ( synchronized ) 两种锁方案
什么是死锁(deadlock)?
360 百科 https://baike.so.com/doc/6950313-7172714.html
死锁 : 是指两个或两个以上的进程在执行过程中, 因争夺资源而造成的一种互相等待的现象, 若无外力作用, 它们都将无法推进下去 .
产生原因:
因为系统资源不足.
进程运行推进顺序不合适.
资源分配不当等.
占用资源的程序崩溃等.
如果系统资源充足, 进程的资源请求都能够得到满足, 死锁出现的可能性就很低, 否则就会因争夺有限的资源而陷入死锁. 其次, 进程运行推进顺序与速度不同, 也可能产生死锁.
下面四个条件是死锁的必要条件, 只要系统发生死锁, 这些条件必然成立, 而只要下列条件之一不满足, 就不会发生死锁.
互斥条件: 一个资源每次只能被一个进程使用.
请求与保持条件: 一个进程因请求资源而阻塞时, 对已获得的资源保持不放.
不剥夺条件: 进程已获得的资源, 在末使用完之前, 不能强行剥夺.
循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系.
死锁的解除与预防:
理解了死锁的原因, 尤其是产生死锁的四个必要条件, 就可以最大可能地避免, 预防和 解除死锁. 所以, 在系统设计, 进程调度等方面注意如何不让这四个必要条件成立, 如何确 定资源的合理分配算法, 避免进程永久占据系统资源. 此外, 也要防止进程在处于等待状态的情况下占用资源. 因此, 对资源的分配要给予合理的规划.
如何确保 N 个线程可以访问 N 个资源同时又不导致死锁?
上面一题我们知道了发生死锁的四个必要条件. 我们只要使其中一个不成立就行了. 一种非常简单的避免死锁的方式就是: 指定获取锁的顺序, 并强制线程按照指定的顺序获取锁. 因此, 如果所有的线程都是以同样的顺序加锁和释放锁, 就不会出现死锁了. 这也就是破坏了第四个条件循环等待条件.
Java 中垃圾回收有什么目的? 什么时候进行垃圾回收?
垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行.
垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源.
finalize()方法什么时候被调用? 析构函数 (finalization) 的目的是什么?
1)垃圾回收器 (garbage colector) 决定回收某对象时, 就会运行该对象的 finalize()方法;
finalize 是 Object 类的一个方法, 该方法在 Object 类中的声明 protected void finalize() throws Throwable { }
在垃圾回收器执行时会调用被回收对象的 finalize()方法, 可以覆盖此方法来实现对其资源的回收. 注意: 一旦垃圾回收器准备释放对象占用的内存, 将首先调用该对象的 finalize()方法, 并且下一次垃圾回收动作发生时, 才真正回收对象占用的内存空间
2)GC 本来就是内存回收了, 应用还需要在 finalization 做什么呢? 答案是大部分时候, 什么都不用做(也就是不需要重载). 只有在某些很特殊的情况下, 比如你调用了一些 native 的方法(一般是 C 写的), 可以要在 finaliztion 里去调用 C 的释放函数.
如果对象的引用被置为 null, 垃圾收集器是否会立即释放对象占用的内存?
不会, 在下一个垃圾回收周期中, 这个对象将是可被回收的.
JavaWeb 基础知识总结
Servlet 总结:
在 Java Web 程序中, Servlet 主要负责接收用户请求 HttpServletRequest, 在 doGet(),doPost()中做相应的处理, 并将回应 HttpServletResponse 反馈给用户. Servlet 可以设置初始化参数, 供 Servlet 内部使用. 一个 Servlet 类只会有一个实例, 在它初始化时调用 init()方法, 销毁时调用 destroy()方法. Servlet 需要在 web.xml 中配置(MyEclipse 中创建 Servlet 会自动配置), 一个 Servlet 可以设置多个 URL 访问. Servlet 不是线程安全, 因此要谨慎使用类变量.
阐述 Servlet 和 CGI 的区别?
CGI 的不足之处:
1, 需要为每个请求启动一个操作 CGI 程序的系统进程. 如果请求频繁, 这将会带来很大的开销.
2, 需要为每个请求加载和运行一个 CGI 程序, 这将带来很大的开销
3, 需要重复编写处理网络协议的代码以及编码, 这些工作都是非常耗时的.
Servlet 的优点:
1, 只需要启动一个操作系统进程以及加载一个 JVM, 大大降低了系统的开销
2, 如果多个请求需要做同样处理的时候, 这时候只需要加载一个类, 这也大大降低了开销
3, 所有动态加载的类可以实现对网络协议以及请求解码的共享, 大大降低了工作量.
4,Servlet 能直接和 Web 服务器交互, 而普通的 CGI 程序不能. Servlet 还能在各个程序之间共享数据, 使数据库连接池之类的功能很容易实现.
补充: Sun Microsystems 公司在 1996 年发布 Servlet 技术就是为了和 CGI 进行竞争, Servlet 是一个特殊的 Java 程序, 一个基于 Java 的 Web 应用通常包含一个或多个 Servlet 类. Servlet 不能够自行创建并执行, 它是在 Servlet 容器中运行的, 容器将用户的请求传递给 Servlet 程序, 并将 Servlet 的响应回传给用户. 通常一个 Servlet 会关联一个或多个 JSP 页面. 以前 CGI 经常因为性能开销上的问题被诟病, 然而 Fast CGI 早就已经解决了 CGI 效率上的问题, 所以面试的时候大可不必信口开河的诟病 CGI, 事实上有很多你熟悉的网站都使用了 CGI 技术.
参考:javaweb 整合开发王者归来P7
Servlet 接口中有哪些方法及 Servlet 生命周期探秘
Servlet 接口定义了 5 个方法, 其中前三个方法与 Servlet 生命周期相关:
- void init(ServletConfig config) throws ServletException
- void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
- void destory()
- java.lang.String getServletInfo()
- ServletConfig getServletConfig()
生命周期: Web 容器加载 Servlet 并将其实例化后, Servlet 生命周期开始, 容器运行其 init()方法进行 Servlet 的初始化; 请求到达时调用 Servlet 的 service()方法, service()方法会根据需要调用与请求对应的 doGet 或 doPost 等方法; 当服务器关闭或项目被卸载时服务器会将 Servlet 实例销毁, 此时会调用 Servlet 的 destroy()方法. init 方法和 destory 方法只会执行一次, service 方法客户端每次请求 Servlet 都会执行. Servlet 中有时会用到一些需要初始化与销毁的资源, 因此可以把初始化资源的代码放入 init 方法中, 销毁资源的代码放入 destroy 方法中, 这样就不需要每次处理客户端的请求都要初始化与销毁资源.
参考:javaweb 整合开发王者归来P81
get 和 post 请求的区别?
get 请求用来从服务器上获得资源, 而 post 是用来向服务器提交数据;
get 将表单中数据按照 name=value 的形式, 添加到 action 所指向的 URL 后面, 并且两者使用 "?" 连接, 而各个变量之间使用 "&" 连接; post 是将表单中的数据放在 HTTP 协议的请求头或消息体中, 传递到 action 所指向 URL;
get 传输的数据要受到 URL 长度限制(1024 字节即 256 个字符); 而 post 可以传输大量的数据, 上传文件通常要使用 post 方式;
使用 get 时参数会显示在地址栏上, 如果这些数据不是敏感数据, 那么可以使用 get; 对于敏感数据还是应用使用 post;
get 使用 MIME 类型 application/x-www-form-urlencoded 的 URL 编码 (也叫百分号编码) 文本的格式传递参数, 保证被传送的参数由遵循规范的文本组成, 例如一个空格的编码是 " ".
补充: GET 方式提交表单的典型应用是搜索引擎. GET 方式就是被设计为查询用的.
什么情况下调用 doGet()和 doPost()?
Form 标签里的 method 的属性为 get 时调用 doGet(), 为 post 时调用 doPost().
转发 (Forward) 和重定向 (Redirect) 的区别?
转发是服务器行为, 重定向是客户端行为.
转发(Forword)
通过 RequestDispatcher 对象的 forward(HttpServletRequest request,HttpServletResponse response)方法实现的. RequestDispatcher 可以通过 HttpServletRequest 的 getRequestDispatcher()方法获得. 例如下面的代码就是跳转到 login_success.jsp 页面.
request.getRequestDispatcher("login_success.jsp").forward(request, response);
重定向 (Redirect) 是利用服务器返回的状态吗来实现的. 客户端浏览器请求服务器的时候, 服务器会返回一个状态码. 服务器通过 HttpServletRequestResponse 的 setStatus(int status) 方法设置状态码. 如果服务器返回 301 或者 302, 则浏览器会到新的网址重新请求该资源.
从地址栏显示来说
forward 是服务器请求资源, 服务器直接访问目标地址的 URL, 把那个 URL 的响应内容读取过来, 然后把这些内容再发给浏览器. 浏览器根本不知道服务器发送的内容从哪里来的, 所以它的地址栏还是原来的地址.
redirect 是服务端根据逻辑, 发送一个状态码, 告诉浏览器重新去请求那个地址. 所以地址栏显示的是新的 URL.
从数据共享来说
forward: 转发页面和转发到的页面可以共享 request 里面的数据.
redirect: 不能共享数据.
从运用地方来说
forward: 一般用于用户登陆的时候, 根据角色转发到相应的模块.
redirect: 一般用于用户注销登陆时返回主页面和跳转到其它的网站等
从效率来说
forward: 高.
redirect: 低.
自动刷新(Refresh)
自动刷新不仅可以实现一段时间之后自动跳转到另一个页面, 还可以实现一段时间之后自动刷新本页面. Servlet 中通过 HttpServletResponse 对象设置 Header 属性实现自动刷新例如:
Response.setHeader("Refresh","1000;URL=http://localhost:8080/servlet/example.htm");
其中 1000 为时间, 单位为毫秒. URL 指定就是要跳转的页面(如果设置自己的路径, 就会实现没过一秒自动刷新本页面一次)
Servlet 与线程安全
Servlet 不是线程安全的, 多线程并发的读写会导致数据不同步的问题. 解决的办法是尽量不要定义 name 属性, 而是要把 name 变量分别定义在 doGet()和 doPost()方法内. 虽然使用 synchronized(name){}语句块可以解决问题, 但是会造成线程的等待, 不是很科学的办法.
注意: 多线程的并发的读写 Servlet 类属性会导致数据不同步. 但是如果只是并发地读取属性而不写入, 则不存在数据不同步的问题. 因此 Servlet 里的只读属性最好定义为 final 类型的.
参考:javaweb 整合开发王者归来P92
JSP 和 Servlet 是什么关系?
其实这个问题在上面已经阐述过了, Servlet 是一个特殊的 Java 程序, 它运行于服务器的 JVM 中, 能够依靠服务器的支持向浏览器提供显示内容. JSP 本质上是 Servlet 的一种简易形式, JSP 会被服务器处理成一个类似于 Servlet 的 Java 程序, 可以简化页面内容的生成. Servlet 和 JSP 最主要的不同点在于, Servlet 的应用逻辑是在 Java 文件中, 并且完全从表示层中的 HTML 分离开来. 而 JSP 的情况是 Java 和 HTML 可以组合成一个扩展名为. jsp 的文件. 有人说, Servlet 就是在 Java 中写 HTML, 而 JSP 就是在 HTML 中写 Java 代码, 当然这个说法是很片面且不够准确的. JSP 侧重于视图, Servlet 更侧重于控制逻辑, 在 MVC 架构模式中, JSP 适合充当视图 (view) 而 Servlet 适合充当控制器(controller).
JSP 工作原理:
JSP 是一种 Servlet, 但是与 HttpServlet 的工作方式不太一样. HttpServlet 是先由源代码编译为 class 文件后部署到服务器下, 为先编译后部署. 而 JSP 则是先部署后编译. JSP 会在客户端第一次请求 JSP 文件时被编译为 HttpJspPage 类(接口 Servlet 的一个子类). 该类会被服务器临时存放在服务器工作目录里面. 下面通过实例给大家介绍.
工程 JspLoginDemo 下有一个名为 login.jsp 的 Jsp 文件, 把工程第一次部署到服务器上后访问这个 Jsp 文件, 我们发现这个目录下多了下图这两个东东.
.class 文件便是 JSP 对应的 Servlet. 编译完毕后再运行 class 文件来响应客户端请求. 以后客户端访问 login.jsp 的时候, Tomcat 将不再重新编译 JSP 文件, 而是直接调用 class 文件来响应客户端请求.
由于 JSP 只会在客户端第一次请求的时候被编译 , 因此第一次请求 JSP 时会感觉比较慢, 之后就会感觉快很多. 如果把服务器保存的 class 文件删除, 服务器也会重新编译 JSP.
开发 Web 程序时经常需要修改 JSP.Tomcat 能够自动检测到 JSP 程序的改动. 如果检测到 JSP 源代码发生了改动. Tomcat 会在下次客户端请求 JSP 时重新编译 JSP, 而不需要重启 Tomcat. 这种自动检测功能是默认开启的, 检测改动会消耗少量的时间, 在部署 Web 应用的时候可以在 web.xml 中将它关掉.
参考:javaweb 整合开发王者归来P97
JSP 有哪些内置对象? 作用分别是什么?
JSP 内置对象 - CSDN 博客 http://blog.csdn.net/qq_34337272/article/details/64310849
JSP 有 9 个内置对象:
request: 封装客户端的请求, 其中包含来自 GET 或 POST 请求的参数;
response: 封装服务器对客户端的响应;
pageContext: 通过该对象可以获取其他对象;
session: 封装用户会话的对象;
application: 封装服务器运行环境的对象;
out: 输出服务器响应的输出流对象;
config:Web 应用的配置对象;
page:JSP 页面本身(相当于 Java 程序中的 this);
exception: 封装页面抛出异常的对象.
Request 对象的主要方法有哪些?
setAttribute(String name,Object): 设置名字为 name 的 request 的参数值
getAttribute(String name): 返回由 name 指定的属性值
getAttributeNames(): 返回 request 对象所有属性的名字集合, 结果是一个枚举的实例
getCookies(): 返回客户端的所有 Cookie 对象, 结果是一个 Cookie 数组
getCharacterEncoding() : 返回请求中的字符编码方式 = getContentLength() : 返回请求的 Body 的长度
getHeader(String name) : 获得 HTTP 协议定义的文件头信息
getHeaders(String name) : 返回指定名字的 request Header 的所有值, 结果是一个枚举的实例
getHeaderNames() : 返回所以 request Header 的名字, 结果是一个枚举的实例
getInputStream() : 返回请求的输入流, 用于获得请求中的数据
getMethod() : 获得客户端向服务器端传送数据的方法
getParameter(String name) : 获得客户端传送给服务器端的有 name 指定的参数值
getParameterNames() : 获得客户端传送给服务器端的所有参数的名字, 结果是一个枚举的实例
getParameterValues(String name): 获得有 name 指定的参数的所有值
getProtocol(): 获取客户端向服务器端传送数据所依据的协议名称
getQueryString() : 获得查询字符串
getRequestURI() : 获取发出请求字符串的客户端地址
getRemoteAddr(): 获取客户端的 IP 地址
getRemoteHost() : 获取客户端的名字
getSession([Boolean create]) : 返回和请求相关 Session
getServerName() : 获取服务器的名字
getServletPath(): 获取客户端所请求的脚本文件的路径
getServerPort(): 获取服务器的端口号
removeAttribute(String name): 删除请求中的一个属性
request.getAttribute()和 request.getParameter()有何区别?
从获取方向来看:
getParameter()是获取 POST/GET 传递的参数值;
getAttribute()是获取对象容器中的数据值;
从用途来看:
getParameter 用于客户端重定向时, 即点击了链接或提交按扭时传值用, 即用于在用表单或 url 重定向传值时接收数据用.
getAttribute 用于服务器端重定向时, 即在 sevlet 中使用了 forward 函数, 或 struts 中使用了
mapping.findForward. getAttribute 只能收到程序用 setAttribute 传过来的值.
另外, 可以用 setAttribute,getAttribute 发送接收对象. 而 getParameter 显然只能传字符串.
setAttribute 是应用服务器把这个对象放在该页面所对应的一块内存中去, 当你的页面服务器重定向到另一个页面时, 应用服务器会把这块内存拷贝另一个页面所对应的内存中. 这样 getAttribute 就能取得你所设下的值, 当然这种方法可以传对象. session 也一样, 只是对象在内存中的生命周期不一样而已. getParameter 只是应用服务器在分析你送上来的 request 页面的文本时, 取得你设在表单或 url 重定向时的值.
总结:
getParameter 返回的是 String, 用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型, 比如整型, 日期类型啊等等)
getAttribute 返回的是 Object, 需进行转换, 可用 setAttribute 设置成任意对象, 使用很灵活, 可随时用
include 指令 include 的行为的区别
include 指令: JSP 可以通过 include 指令来包含其他文件. 被包含的文件可以是 JSP 文件, HTML 文件或文本文件. 包含的文件就好像是该 JSP 文件的一部分, 会被同时编译执行. 语法格式如下:
<%@ include file="文件相对 url 地址" %>
include 动作: 动作元素用来包含静态和动态的文件. 该动作把指定文件插入正在生成的页面. 语法格式如下:
JSP 九大内置对象, 七大动作, 三大指令 -
JSP 九大内置对象, 七大动作, 三大指令总结 http://blog.csdn.net/qq_34337272/article/details/64310849
讲解 JSP 中的四种作用域
JSP 中的四种作用域包括 page,request,session 和 application, 具体来说:
page 代表与一个页面相关的对象和属性.
request 代表与 Web 客户机发出的一个请求相关的对象和属性. 一个请求可能跨越多个页面, 涉及多个 Web 组件; 需要在页面显示的临时数据可以置于此作用域.
session 代表与某个用户与服务器建立的一次会话相关的对象和属性. 跟某个用户相关的数据应该放在用户自己的 session 中.
application 代表与整个 Web 应用程序相关的对象和属性, 它实质上是跨越整个 Web 应用程序, 包括多个页面, 请求和会话的一个全局作用域.
如何实现 JSP 或 Servlet 的单线程模式?
对于 JSP 页面, 可以通过 page 指令进行设置.
<%@page isThreadSafe="false"%>
对于 Servlet, 可以让自定义的 Servlet 实现 SingleThreadModel 标识接口.
说明: 如果将 JSP 或 Servlet 设置成单线程工作模式, 会导致每个请求创建一个 Servlet 实例, 这种实践将导致严重的性能问题(服务器的内存压力很大, 还会导致频繁的垃圾回收), 所以通常情况下并不会这么做.
实现会话跟踪的技术有哪些?
使用 Cookie
向客户端发送 Cookie
- Cookie c =new Cookie("name","value"); // 创建 Cookie
- c.setMaxAge(60*60*24); // 设置最大时效, 此处设置的最大时效为一天
- response.addCookie(c); // 把 Cookie 放入到 HTTP 响应中
从客户端读取 Cookie
- String name ="name";
- Cookie[]cookies =request.getCookies();
- if(cookies !=null){
- for(int i= 0;i<cookies.length;i++){
- Cookie cookie =cookies[i];
- if(name.equals(cookis.getName()))
- //something is here.
- //you can get the value
- cookie.getValue();
- }
- }
优点: 数据可以持久保存, 不需要服务器资源, 简单, 基于文本的 Key-Value
缺点: 大小受到限制, 用户可以禁用 Cookie 功能, 由于保存在本地, 有一定的安全风险.
URL 重写
在 URL 中添加用户会话的信息作为请求的参数, 或者将唯一的会话 ID 添加到 URL 结尾以标识一个会话.
优点: 在 Cookie 被禁用的时候依然可以使用
缺点: 必须对网站的 URL 进行编码, 所有页面必须动态生成, 不能用预先记录下来的 URL 进行访问.
3. 隐藏的表单域
<input type="hidden" name ="session" value="..."/>
优点: Cookie 被禁时可以使用
缺点: 所有页面必须是表单提交之后的结果.
HttpSession
在所有会话跟踪技术中, HttpSession 对象是最强大也是功能最多的. 当一个用户第一次访问某个网站时会自动创建 HttpSession, 每个用户可以访问他自己的 HttpSession. 可以通过 HttpServletRequest 对象的 getSession 方 法获得 HttpSession, 通过 HttpSession 的 setAttribute 方法可以将一个值放在 HttpSession 中, 通过调用 HttpSession 对象的 getAttribute 方法, 同时传入属性名就可以获取保存在 HttpSession 中的对象. 与上面三种方式不同的 是, HttpSession 放在服务器的内存中, 因此不要将过大的对象放在里面, 即使目前的 Servlet 容器可以在内存将满时将 HttpSession 中的对象移到其他存储设备中, 但是这样势必影响性能. 添加到 HttpSession 中的值可以是任意 Java 对象, 这个对象最好实现了 Serializable 接口, 这样 Servlet 容器在必要的时候可以将其序列化到文件中, 否则在序列化时就会出现异常.
Cookie 和 Session 的的区别?
由于 HTTP 协议是无状态的协议, 所以服务端需要记录用户的状态时, 就需要用某种机制来识具体的用户, 这个机制就是 Session. 典型的场景比如购物车, 当你点击下单按钮时, 由于 HTTP 协议无状态, 所以并不知道是哪个用户操作的, 所以服务端要为特定的用户创建了特定的 Session, 用用于标识这个用户, 并且跟踪用户, 这样才知道购物车里面有几本书. 这个 Session 是保存在服务端的, 有一个唯一标识. 在服务端保存 Session 的方法很多, 内存, 数据库, 文件都有. 集群的时候也要考虑 Session 的转移, 在大型的网站, 一般会有专门的 Session 服务器集群, 用来保存用户会话, 这个时候 Session 信息都是放在内存的, 使用一些缓存服务比如 Memcached 之类的来放 Session.
思考一下服务端如何识别特定的客户? 这个时候 Cookie 就登场了. 每次 HTTP 请求的时候, 客户端都会发送相应的 Cookie 信息到服务端. 实际上大多数的应用都是用 Cookie 来实现 Session 跟踪的, 第一次创建 Session 的时候, 服务端会在 HTTP 协议中告诉客户端, 需要在 Cookie 里面记录一个 Session ID, 以后每次请求把这个会话 ID 发送到服务器, 我就知道你是谁了. 有人问, 如果客户端的浏览器禁用了 Cookie 怎么办? 一般这种情况下, 会使用一种叫做 URL 重写的技术来进行会话跟踪, 即每次 HTTP 交互, URL 后面都会被附加上一个诸如 sid=xxxxx 这样的参数, 服务端据此来识别用户.
Cookie 其实还可以用在一些方便用户的场景下, 设想你某次登陆过一个网站, 下次登录的时候不想再次输入账号了, 怎么办? 这个信息可以写到 Cookie 里面, 访问网站的时候, 网站页面的脚本可以读取这个信息, 就自动帮你把用户名给填了, 能够方便一下用户. 这也是 Cookie 名称的由来, 给用户的一点甜头. 所以, 总结一下: Session 是在服务端保存的一个数据结构, 用来跟踪用户的状态, 这个数据可以保存在集群, 数据库, 文件中; Cookie 是客户端保存用户信息的一种机制, 用来记录用户的一些信息, 也是实现 Session 的一种方式.
Java 集合篇常见问题
List,Set,Map 三者的区别及总结
List: 对付顺序的好帮手
List 接口存储一组不唯一(可以有多个元素引用相同的对象), 有序的对象
Set: 注重独一无二的性质
不允许重复的集合. 不会有多个元素引用相同的对象.
Map: 用 Key 来搜索的专家
使用键值对存储. Map 会维护与 Key 有关联的值. 两个 Key 可以引用相同的对象, 但 Key 不能重复, 典型的 Key 是 String 类型, 但也可以是任何对象.
Arraylist 与 LinkedList 区别
Arraylist 底层使用的是数组 (存读数据效率高, 插入删除特定位置效率低),LinkedList 底层使用的是双向循环链表数据结构(插入, 删除效率特别高). 学过数据结构这门课后我们就知道采用链表存储, 插入, 删除元素时间复杂度不受元素位置的影响, 都是近似 O(1) 而数组为近似 O(n), 因此当数据特别多, 而且经常需要插入删除元素时建议选用 LinkedList. 一般程序只用 Arraylist 就够用了, 因为一般数据量都不会蛮大, Arraylist 是使用最多的集合类.
ArrayList 与 Vector 区别(为什么要用 Arraylist 取代 Vector 呢?)
Vector 类的所有方法都是同步的. 可以由两个线程安全地访问一个 Vector 对象, 但是一个线程访问 Vector
, 代码要在同步操作上耗费大量的时间. Arraylist 不是同步的, 所以在不需要同步时建议使用 Arraylist.
HashMap 和 Hashtable 的区别
HashMap 是非线程安全的, HashTable 是线程安全的; HashTable 内部的方法基本都经过 synchronized 修饰.
因为线程安全的问题, HashMap 要比 HashTable 效率高一点, HashTable 基本被淘汰.
HashMap 允许有 null 值的存在, 而在 HashTable 中 put 进的键值只要有一个 null, 直接抛出 NullPointerException.
Hashtable 和 HashMap 有几个主要的不同: 线程安全以及速度. 仅在你需要完全的线程安全的时候使用 Hashtable, 而如果你使用 Java5 或以上的话, 请使用 ConcurrentHashMap 吧
HashSet 和 HashMap 区别
HashMap 和 ConcurrentHashMap 的区别
HashMap 与 ConcurrentHashMap 的区别 https://blog.csdn.net/xuefeng0707/article/details/40834595
ConcurrentHashMap 对整个桶数组进行了分割分段(Segment), 然后在每一个分段上都用 lock 锁进行保护, 相对于 HashTable 的 synchronized 锁的粒度更精细了一些, 并发性能更好, 而 HashMap 没有锁机制, 不是线程安全的.(JDK1.8 之后 ConcurrentHashMap 启用了一种全新的方式实现, 利用 CAS 算法.)
HashMap 的键值对允许有 null, 但是 ConCurrentHashMap 都不允许.
HashSet 如何检查重复
当你把对象加入 HashSet 时, HashSet 会先计算对象的 hashcode 值来判断对象加入的位置, 同时也会与其他加入的对象的 hashcode 值作比较, 如果没有相符的 hashcode,HashSet 会假设对象没有重复出现. 但是如果发现有相同 hashcode 值的对象, 这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同. 如果两者相同, HashSet 就不会让加入操作成功.(摘自我的 Java 启蒙书Head fist java第二版)
hashCode()与 equals()的相关规定:
如果两个对象相等, 则 hashcode 一定也是相同的
两个对象相等, 对两个 equals 方法返回 true
两个对象有相同的 hashcode 值, 它们也不一定是相等的
综上, equals 方法被覆盖过, 则 hashCode 方法也必须被覆盖
hashCode()的默认行为是对堆上的对象产生独特值. 如果没有重写 hashCode(), 则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据).
== 与 equals 的区别
== 是判断两个变量或实例是不是指向同一个内存空间 equals 是判断两个变量或实例所指向的内存空间的值是不是相同
== 是指对内存地址进行比较 equals()是对字符串的内容进行比较 3.== 指引用是否相同 equals()指的是值是否相同
comparable 和 comparator 的区别?
comparable 接口实际上是出自 java.lang 包 它有一个 compareTo(Object obj)方法用来排序
comparator 接口实际上是出自 java.util 包它有一个 compare(Object obj1, Object obj2)方法用来排序
一般我们需要对一个集合使用自定义排序时, 我们就要重写 compareTo 方法或 compare 方法, 当我们需要对某一个集合实现两种排序方式, 比如一个 song 对象中的歌名和歌手名分别采用一种排序方法的话, 我们可以重写 compareTo 方法和使用自制的 Comparator 方法或者以两个 Comparator 来实现歌名排序和歌星名排序, 第二种代表我们只能使用两个参数版的 Collections.sort().
Comparator 定制排序
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Comparator;
- public class CollectionsSort {
- public static void main(String[] args) {
- ArrayList<Integer> arrayList = new ArrayList<Integer>();
- arrayList.add(-1);
- arrayList.add(3);
- arrayList.add(3);
- arrayList.add(-5);
- arrayList.add(7);
- arrayList.add(4);
- arrayList.add(-9);
- arrayList.add(-7);
- System.out.println("原始数组:");
- System.out.println(arrayList);
- // void reverse(List list): 反转
- Collections.reverse(arrayList);
- System.out.println("Collections.reverse(arrayList):");
- System.out.println(arrayList);
- /*
- * void rotate(List list, int distance), 旋转.
- * 当 distance 为正数时, 将 list 后 distance 个元素整体移到前面. 当 distance 为负数时, 将
- * list 的前 distance 个元素整体移到后面.
- Collections.rotate(arrayList, 4);
- System.out.println("Collections.rotate(arrayList, 4):");
- System.out.println(arrayList);*/
- // void sort(List list), 按自然排序的升序排序
- Collections.sort(arrayList);
- System.out.println("Collections.sort(arrayList):");
- System.out.println(arrayList);
- // void shuffle(List list), 随机排序
- Collections.shuffle(arrayList);
- System.out.println("Collections.shuffle(arrayList):");
- System.out.println(arrayList);
- // 定制排序的用法
- Collections.sort(arrayList, new Comparator<Integer>() {
- @Override
- public int compare(Integer o1, Integer o2) {
- return o2.compareTo(o1);
- }
- });
- System.out.println("定制排序后:");
- System.out.println(arrayList);
- }
- }
重写 compareTo 方法实现按年龄来排序
- package map;
- import java.util.Set;
- import java.util.TreeMap;
- public class TreeMap2 {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- TreeMap<Person, String> pdata = new TreeMap<Person, String>();
- pdata.put(new Person("张三", 30), "zhangsan");
- pdata.put(new Person("李四", 20), "lisi");
- pdata.put(new Person("王五", 10), "wangwu");
- pdata.put(new Person("小红", 5), "xiaohong");
- // 得到 key 的值的同时得到 key 所对应的值
- Set<Person> keys = pdata.keySet();
- for (Person key : keys) {
- System.out.println(key.getAge() + "-" + key.getName());
- }
- }
- }
- // person 对象没有实现 Comparable 接口, 所以必须实现, 这样才不会出错, 才可以使 treemap 中的数据按顺序排列
- // 前面一个例子的 String 类已经默认实现了 Comparable 接口, 详细可以查看 String 类的 API 文档, 另外其他
- // 像 Integer 类等都已经实现了 Comparable 接口, 所以不需要另外实现了
- class Person implements Comparable<Person> {
- private String name;
- private int age;
- public Person(String name, int age) {
- super();
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- /**
- * TODO 重写 compareTo 方法实现按年龄来排序
- */
- @Override
- public int compareTo(Person o) {
- // TODO Auto-generated method stub
- if (this.age> o.getAge()) {
- return 1;
- } else if (this.age <o.getAge()) {
- return -1;
- }
- return age;
- }
- }
如何对 Object 的 list 排序?
对 objects 数组进行排序, 我们可以用 Arrays.sort()方法
对 objects 的集合进行排序, 需要使用 Collections.sort()方法
如何实现数组与 List 的相互转换?
List 转数组: toArray(arraylist.size()方法; 数组转 List:Arrays 的 asList(a)方法
- List<String> arrayList = new ArrayList<String>();
- arrayList.add("s");
- arrayList.add("e");
- arrayList.add("n");
- /**
- * ArrayList 转数组
- */
- int size=arrayList.size();
- String[] a = arrayList.toArray(new String[size]);
- // 输出第二个元素
- System.out.println(a[1]);// 结果: e
- // 输出整个数组
- System.out.println(Arrays.toString(a));// 结果:[s, e, n]
- /**
- * 数组转 list
- */
- List<String> list=Arrays.asList(a);
- /**
- * list 转 Arraylist
- */
- List<String> arrayList2 = new ArrayList<String>();
- arrayList2.addAll(list);
- System.out.println(list);
如何求 ArrayList 集合的交集 并集 差集 去重复并集
需要用到 List 接口中定义的几个方法:
addAll(Collection<? extends E> c) : 按指定集合的 Iterator 返回的顺序将指定集合中的所有元素追加到此列表的末尾
实例代码:
retainAll(Collection<?> c): 仅保留此列表中包含在指定集合中的元素.
removeAll(Collection<?> c) : 从此列表中删除指定集合中包含的所有元素.
- package list;
- import java.util.ArrayList;
- import java.util.List;
- public class MethodDemo {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- List<Integer> list1 = new ArrayList<Integer>();
- list1.add(1);
- list1.add(2);
- list1.add(3);
- list1.add(4);
- List<Integer> list2 = new ArrayList<Integer>();
- list2.add(2);
- list2.add(3);
- list2.add(4);
- list2.add(5);
- // 并集
- // list1.addAll(list2);
- // 交集
- //list1.retainAll(list2);
- // 差集
- // list1.removeAll(list2);
- // 无重复并集
- list2.removeAll(list1);
- list1.addAll(list2);
- for (Integer i : list1) {
- System.out.println(i);
- }
- }
- }
HashMap 的工作原理及代码实现
集合框架源码学习之 HashMap(JDK1.8) https://juejin.im/post/5ab0568b5188255580020e56
ConcurrentHashMap 的工作原理及代码实现
ConcurrentHashMap 实现原理及源码分析
参考书籍:
来源: https://www.cnblogs.com/LexMoon/p/javams.html