Java 题目
2018.7.15
多个线程可同时操作一个数据,为了保证该数据的准确性,可将操作数据的部分改为 同步
- 同步:进程之间的关系不是相互排除临界资源的关系,而是相互依赖的关系。进一步说明就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。
- 异步:异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。
JSP 四大作用区:page、request、session、application
- 存储在application对象中的属性可以被同一个WEB应用程序中所有的Servlet 和 Jsp 页面访问。(属性作用范围最大)
- 存储在session对象中的属性可以被属于同一个会话(浏览器打开直到关闭称为一次会话,且在此期间会话不失效)的所有Servlet 和 Jsp 页面访问
- 存储在
request对象中的属性可以被属于同一个请求的所有Servlet和JSP页面访问(在有转发的情况下可以跨页面获取属性值),例如使用PageContext.forward和PageContext.include方法连接起来的多个Servlet和JSP页面。 - 存储在
pageContext对象中的属性仅可以被当前JSP页面的当前响应过程中调用的各个组件访问,例如,正在响应当前请求的JSP页面和它调用的各个自定义标签类。
ServletConfig 接口默认是 GenericServlet 实现的(自己看源码结构吧)
关于JVM内存一些概念的叙述:
- 程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,是线程隔离的
- Java 方法执行在内存模型,用于存储局部变量,操作数栈,动态链接,方法出口等信息,是线程隔离的
- 方法区用于存储JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,是线程共享的(看到静态变量就要想到线程共享啊)
- 原则上讲,所有的对象都在堆内存上分配,是线程共享的
对象序列化:
- 要使流可以传输对象,则对象需要实现Serializeable接口,使用ObjectInputStream 和 ObjectOutputStream 便可以实现
- 使用 transient 修饰的变量不会被初始化
- 对象序列化的所属类需要实现Serializable 接口
线程安全的集合:vector,stack,hashtable,enumeration,除此之外均是非线程安全的类与接口
可以这么记忆:喂!SHE! 喂是指 vector,S是指 stack, H是指 hashtable,E是指:Eenumeration
以下JSP代码定义了一个变量,输出这个变量的值:
<bean:define id=”stringBean” value=”helloWorld”/>
1
<%=stringBean%>
1
<bean:write name="stringBean"/>
1
2<%String myBean=(String)pageContext.getAttribute("stringBean",PageContext.PAGE_SCOPE);%>
<%=myBean%>
2018.7.17
管道(Pipe)通信的叙述
- 进程对管道进行读操作和写操作都可能被阻塞:当管道中没有信息的时候,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也会自动消失。
- 管道只支持具有亲缘关系的两个进程之间的通信,比如父进程和子进程
- 一个管道不可以实现双向传输,匿名管道只能直线单向传输,要想实现双向传输的话可以建立两个管道
- 管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程消失管道也对应消失
HashMap 和 HashTable
1
2
3
4
5
6
7
8
9
10
11//HashMap的源码
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
-----------------------------------
//Hashtable的源码
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
----------------------------------
很明显,都实现了Map接口1
2
3
4
5public V put(K key, V value) //HashMap的put方法,没有同步
public synchronized V put(K key, V value) //Hashtable的put方法
//当然,Hashtable的其他方法,如get,size,remove等方法,
//都加了synchronized关键词同步操作1
2
3
4
5
6
7
8
9
10//Hashtable的put方法有以下语句块,大伙看了都知道
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
//那么,我们再来看下HashMap的put方法中,有如下语句
//调用某个方法直接把key为null,值为value的键值对插入进去。
if (key == null)
return putForNullKey(value);1
2
3
4
5
6
7
8//以下是Hashtable的方法
public synchronized boolean contains(Object value)
public synchronized boolean containsKey(Object key)
public boolean containsValue(Object value)
//以下是HashMap中的方法,注意,没有contains方法
public boolean containsKey(Object key)
public boolean containsValue(Object value)
Java 面向对象基础
- java为单继承,多实现。可以实现多个接口。
- 接口允许定义成员,但必须是常量 。
- 抽象类和接口类的无法实例化,任何编译器中直接使用new会报错。
2018.7.18
Java Socket 获取IP地址
本地IP地址:getLocalAddress()
连接IP地址:getInetAddress()
getReuseAddress() 测试是否启用 SO_REUSEADDR
关于 SO_REUSEADDR : 点我了解
Java 基本类型:如果不明确指定,整数型的默认的 int 类型,带小数的默认是 double 类型
Java 语言性特点
Java 致力于检查程序在编译和运行时的错误
Java 能运行虚拟机实现跨平台
Java 自己操作内存减少了内存出错的可能性
程序设计语言中,数组元素在内存中是一个接着一个线性存放的,通过第一个元素就能访问随后的元素,这样的数组称之为“真数组”。
实现了真数组为Java语言健壮性的特点之一。Java 实现了真数组,避免了覆盖数据的可能(而不是覆盖数据类型的可能)
Sql 获取结果集的两种方式:Statement 和 PrepareStatement
- Statement 创建是不传参的
- PrepareStatement 是需要传入 sql 语句的
JDK提供用于并发编程的同步器
- Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
- CyclicBarrier 主要的方法就是一个:await()。await() 方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。
- 倒计数(CountDown)门闩(Latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 CountDownLatch.await() 方法在倒计数为0之前会阻塞当前线程。
2018.7.20
| 作用域 | 当前类 | 同一包 | 子孙类 | 其他包 |
| :——– | :— | —- | —- | —- |
| public | √ | √ | √ | √ |
| protected | √ | √ | √ | |
| default | √ | √ | | |
| private | √ | | | |JDK8开始,接口中可以定义有方法体的方法,方法必须被default和static修饰。除此之外,其他方法都是抽象方法。
Java的语法是单继承,但是继承可以传递。其实B更准确一点是只能有一个直接父类。
继承用extends,实现用implements。先继承后实现。
类是class,接口是interface。
流媒体 技术的一种可以使音频、视频和其他多媒体信息在Internet 及 Internet 上以实时的,无需下载等待的方式进行播放的技术
关于 structs 框架
- structs 可以进行文件上传
- structs 基于 MVC 模式,MVC是模型、视图、控制器,是一种设计模式
- structs 框架让流程结构更清晰
- structs 需要很多 action 类,会增加文件数目
关于java Object 默认的基本方法
- equals(Object obj) 指示某个其他对象是否与此对象“相等”
- Object 对象没有copy() 这个方法
- wait() 导致当前线程的等待,直到其它线程调用此对象的notify() 方法或者 notifyAll() 方法
在 Web 开发中实现会话跟踪
会话跟踪是一种灵活、轻便的机制,它使Web上的状态编程变为可能。
HTTP是一种无状态协议,每当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的、非连续的。当用户在同一网站的多个页面 之间转换时,根本无法确定是否是同一个客户,会话跟踪技术就可以解决这个问题。当一个客户在多个页面间切换时,服务器会保存该用户的信息。
有四种方法可以实现会话跟踪技术:URL重写、隐藏表单域、Cookie、Session。- 隐藏表单域:,非常适合步需要大量数据存储的会话应用。
- URL 重写:URL 可以在后面附加参数,和服务器的请求一起发送,这些参数为名字/值对。
- Cookie:一个 Cookie 是一个小的,已命名数据元素。服务器使用 SET-Cookie 头标将它作为 HTTP响应的一部分传送到客户端,客户端被请求保存 Cookie 值,在对同一服务器的后续请求使用一个Cookie 头标将之返回到服务器。与其它技术比较,Cookie 的一个优点是在浏览器会话结束后,甚至在客户端计算机重启后它仍可以保留其值。
- Session:使用 setAttribute(String str,Object obj)方法将对象捆绑到一个会话
2018.7.21
- 类方法
- 在类方法中不可以用 this 来调用本类的类方法(将 this 理解为对象,而类方法属于类不属于对象,所以类方法前面不能加 this 指针)
- 在类方法中调用本类的类方法时可以直接调用
- 在类方法中可以调用实例方法
2018.7.22
面向对象的五大基本原则
- 单一职责原则(SRP):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的隐申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
- 开放封闭原则(OCP):软件实体应该是可扩展的,而不可修改的。也就是对外扩展开放,对内修改封闭的。
- Liskov替换原则(LSP):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替代基类时,才能保证系统在运行期间识别子类,这是保证继承复用的基础。
- 依赖倒置原则(DIP):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
- 接口隔离原则(ISP):使用多个小的专门接口,而不要使用一个大的总接口。
内部类实例化
1
2
3
4
5
6
7
8
9
10
11
12
13public class Enclosingone {
//非静态内部类
public class InsideOne {}
//静态内部类
public static class InsideTwo{}
}
class Mytest02{
public static void main(String args []){
Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
}
}
2018.7.23
有一个源代码,只包含import java.util.* ; 这一个import语句:
能访问 Java.util目录下的所有类,但不能访问 Java.util 子目录下的所有类,因为如果 Java.util 里面有个a类,Java.util.regex里面也有一个a类,我们若是要调用a类的方法或属性时,就不知道使用哪个a类
关于枚举类的知识
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20enum AccountType
{
SAVING, FIXED, CURRENT;
private AccountType()
{
System.out.println(“It is a account type”);
}
}
class EnumOne
{
public static void main(String[]args)
{
System.out.println(AccountType.FIXED);
}
// 输出结果为:It is a account type
// It is a account type
// It is a account type
// FIXED
}枚举类在后台实现时,实际上是转化为一个继承了 java.lang.Enum类的实体类,原先的枚举类型变成对应的实体类型。上述例子中 AccountType 将会转换成Class AccountType,并且会生成新的构造函数,假如原来有构造函数,则在此基础上添加两个参数,生成新的构造函数。所以上述例子的构造函数将变为:
1
2
3
4pricate AccountType(String s, int i){
super(s, i);
System.out.println(“It is a account type”);
}我们看到Enum 类的源码,这两个参数分别代表 枚举变量和名称 和 这个枚举变量在声明中的位置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}在这个类中,会添加若干个字段来代表具体的枚举类型:
1
2
3public static final AccountType SAVING ;
public static final AccountType FIXED ;
public static final AccountType CURRENT ;而且还会添加一段 static 代码段:
1
2
3
4
5
6
7static {
SAVING = new AccountType("SAVING", 0);
FIXED = new AccountType("FIXED", 0);
CURRENT = new AccountType("CURRENT", 0);
$VALUES = new AccoutType[]{SAVING, FIXED, CURRENT}
}以此来初始化枚举中的每个具体类型。(并将所有具体类型放到一个$VALUE数组中,以便用序号访问具体类型)
在初始化过程中new AccountType构造函数被调用了三次,所以Enum中定义的构造函数中的打印代码被执行了3遍。
以下代码的输出结果是
1 | public class B |
结果为:构造块 构造块 静态块 构造块
开始时JVM加载B.class,对所有的静态成员进行声明,t1 t2被初始化为默认值,为null,又因为t1 t2需要被显式初始化,所以对t1进行显式初始化,初始化代码块→构造函数(没有就是调用默认的构造函数),咦!静态代码块咋不初始化?因为在开始时已经对static部分进行了初始化,虽然只对static变量进行了初始化,但在初始化t1时也不会再执行static块了,因为JVM认为这是第二次加载类B了,所以static会在t1初始化时被忽略掉,所以直接初始化非static部分,也就是构造块部分(输出’’构造块’’)接着构造函数(无输出)。接着对t2进行初始化过程同t1相同(输出’构造块’),此时就对所有的static变量都完成了初始化,接着就执行static块部分(输出’静态块’),接着执行,main方法,同样也,new了对象,调用构造函数输出(’构造块’)
2018.7.25
- 使用Float创建的三种方法
1 | //自动装箱拆箱方法 |
- Servlet 和 CGI 的描述
- 对比一:当用户浏览器发出一个Http/CGI的请求,或者说调用一个CGI程序的时候,服务器端就要新启用一个进程 (而且是每次都要调用),调用CGI程序越多(特别是访问量高的时候),就要消耗系统越多的处理时间,只剩下越来越少的系统资源,对于用户来说,只能是漫长的等待服务器端的返回页面了,这对于电子商务激烈发展的今天来说,不能不说是一种技术上的遗憾。而Servlet充分发挥了服务器端的资源并高效的利用。每次调用Servlet时并不是新启用一个进程 **,而是在一个Web服务器的进程敏感词享和分离线程,而线程最大的好处在于可以共享一个数据源,使系统资源被有效利用。
- 对比二:传统的CGI程序,不具备平台无关性特征,系统环境发生变化,CGI程序就要瘫痪,而Servlet具备Java的平台无关性,在系统开发过程中保持了系统的可扩展性、高效性。
- 对比三:传统技术中,一般大都为二层的系统架构,即Web服务器+数据库服务器,导致网站访问量大的时候,无法克服CGI程序与数据库建立连接时速度慢的瓶颈,从而死机、数据库死锁现象频繁发生。而我们的Servlet有连接池的概念,它可以利用多线程的优点,在系统缓存中事先建立好若干与数据库的连接,到时候若想和数据库打交道可以随时跟系统”要”一个连接即可,反应速度可想而知。
- 方法重写的两同两小一大规则
- 方法名相同,参数类型相同
- 子类返回类型小于等于父类方法返回类型
- 子类抛出异常小于等于父类方法抛出异常
- 子类访问权限大于等于父类方法访问权限
2018.7.26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class C {
C() {
System.out.print("C");
}
}
class A {
C c = new C();
A() {
this("A");
System.out.print("A");
}
A(String s) {
System.out.print(s);
}
}
class Test extends A {
Test() {
super("B");
System.out.print("B");
}
public static void main(String[] args) {
new Test();
}
}初始化过程:
(1)首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
(2)然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
(3)其次,初始化父类中的普通成员变量和代码块,再执行父类的构造方法;
(4)最后,初始化子类的普通成员变量和代码块,再执行子类的构造方法
所以可以看到,main方法运行的时候先找初始化A类的普通成员变量和代码块(父类和子类都没有静态成员变量和代码块),也就是运行了C c = new C( )。
之后初始化构造方法A(String B),最后再打印出B,所以打印出“CBB”
可以把任何一种数据类型的变量赋给 Object 类型的变量
在 Java 中,constructor 必须与 class 同名,方法也可以与 class 同名
2018.7.27
Vector相当于一个线程安全的List
HashMap是非线程安全的,其对应的线程安全类是HashTable
Arraylist是非线程安全的,其对应的线程安全类是Vector
StringBuffer是线程安全的,相当于一个线程安全的StringBuilder
Properties实现了Map接口,是线程安全的
2018.7.28
Object 类的方法

静态内部类不可以直接访问外围类的非静态数据,而非静态内部类可以直接访问外围类的数据,包括私有数据。
2018.7.30
Java 异常机制
运行时异常: 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。