软件开发基础(Java)复习
选择,填空,判断(50分)
1、list,set, map
软件 = 数据结构 + 算法 + 软件架构
![image-20210117153911931](https://i-blog.csdnimg.cn/blog_migrate/fd8c0fe774f7f4a9444a26bce3b0fcaf.png)
List概述
List子接口最大的功能是里面保存的数据可以存在重复内容,并且在Collection子接口中List子接口是最为常用的一个子接口,在List接口中对Collection接口的功能进行了扩充。
![]()
Set子接口
在Collection接口下又有另外一个比较常用的子接口为Set子接口,但是Set子接口并不像List子接口那样对于Collection接口进行了大量的扩充,而是简单的继承了Collection接口。就是说Set子接口里面无法使用get()方法根据索引取得保存数据的操作。在Set子接口下有两个常用的子类(不允许有重复数据,重复数据会自动覆盖先前存在的数据):
- HashSet
- TreeSet:TreeSet子类保存的内容可以进行排序,但是其排序 的依据是依据比较器接口(Comparable)实现的, 即——如果现在要想利用TreeSet子类保存任意类的对 象,那么该对象所在的类必须要实现 java.lang.Comparable接口(java.lang.Comparable接口)。
关于重复元素
- 在支持排序类集操作环境下,TreeSet利用Comparable接口实现重复元素的判断。
- 其他子类像HashSet要消除重复数据,要使用Object类中所提供的两个方法
- public int hashCode() 获取哈希码
- public boolean equals(Object obj) 对象比较
集合遍历
- Iterator
- ListIterator
- Enumeration
- foreach
Map接口
Collection每次只能保存一个对象,所以属于单值保存父接口。而在类集合中又提供了保存偶对象的集合:Map集合,利用Map结合可以保存一对关联数据(按照“key = value”的形式),可以实现根据key取得value的操作。
![]()
![]()
2、反射,class类及对象,
3、泛型
2、3两大部分通过看课件效果可能更好。
4、函数式接口
函数式接口
![]()
// 使用功能型函数式接口——接收参数并返回一个处理结果 // 对应有参数有返回值 import java.util.function.Function; public class TestDemo { public static void main(String[] args) { Function<String, Boolean> fun = "##yootk"::startsWith; System.out.println(fun.apply("##")); // 相当于利用对象调用startsWith() } } //执行结果:true // 消费型接口 // 对应有参数无返回值 import java.util.function.Consumer; public class TestDemo { public static void main(String[] args) { Consumer<String> cons = System.out::print; cons.accept("更多课程,请访问:www.yootk.com"); } } // 执行结果:更多课程,请访问:www.yootk.com // 供给型接口 // 对应无参数有返回值 import java.util.function.Supplier; public class TestDemo { public static void main(String[] args) { Supplier<String> sup = "yootk"::toUpperCase; System.out.println(sup.get()); } } // 执行结果:YOOTK // 断言型接口 // 对应判断真假,也属于无参数有返回值 import java.util.function.Predicate; public class TestDemo { public static void main(String[] args) { Predicate<String> pre = "yootk"::equalsIgnoreCase; System.out.println(pre.test("YOOTK")); } } // 执行结果:true
5、枚举,Java常用类
枚举
enum Color { // 定义枚举类 RED, GREEN, BLUE; // 表示此处为实例化对象 } public class TestDemo { public static void main(String[] args) { Color red = Color.RED; // 直接取得枚举对像 System.out.println(red); } } // 程序执行结果:RED
![]()
enum Color { // 定义枚举类 RED, GREEN, BLUE; // 表示此处为实例化对象 } public class TestDemo { public static void main(String[] args) { Color red = Color.RED; // 直接取得枚举对象 System.out.println("枚举对象序号:" + red.ordinal()); System.out.println("枚举对象名称:" + red.name()); } } // 程序执行的结果为: //枚举对象序号:0 //枚举对象名称:RED
// 扩充枚举的功能 enum Color { RED("红色"), GREEN("绿色"), BLUE("蓝色"); // 定义枚举对象,必须写在首行 private String title; // 属性 private Color(String title) { // 构造方法,不能使用public声明 this.title = title; } public String toString() { // 覆写toString()方法 return this.title; } } public class TestDemo { public static void main(String[] args) { for (Color c : Color.values()) { // 取得全部枚举对象 System.out.print(c + "、"); // 直接输出对象调toString() } } } // 执行结果为:红色、绿色、蓝色、
Java常用类库
CharSequence是从JDK 1.4开始提供的一个描述字符串标准的接口,常用的子类有是三个:String、StringBuffer、StringBuilder
![]()
String类和StringBuffer类的区别是什么?StringBuffer类提供了那些独特的方法?
答:
- String类的内容一旦声明则不可修改,而StringBuffer类的内容定义之后可以修改。StringBuffer类使用append()方法可以完成字符串的连接操作,而String类使用“+”完成;
- 特殊方法:insert()、reverse()、replace()。
AutoCloseable :从JDK 1.7 版本开始提供有一个AutoCloseable接口,该接口的主要功能是结合异常处理结构在资源操作完成后实现自动释放功能,该接口定义如下:
public interface AutoCloseable { public void close() throws Exception; // 资源释放 }
![]()
Runtime:描述的是运行时的状态,在每一个JVM进程之中都会提供唯一的一个Runtime类实例对象,开发者可以通过Runtime类对象获取与JVM有关的运行状态。
![]()
![]()
// 获取本机的CPU处理器的数量 public class JavaAPIDemo { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime() ; // 获取Runtime实例化对象 System.out.println(runtime.availableProcessors()); // 获取处理器数量 } } // 获取主机内存信息 public class JavaAPIDemo { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); // 获取实例化对象 System.out.println("MAX_MEMORY:" + runtime.maxMemory()); // 获取最大 可用内存 System.out.println("TOTAL_MEMORY:" + runtime.totalMemory()); // 获取 可用内存 System.out.println("FREE_MEMORY:" + runtime.freeMemory()); // 获取空 闲内存 } } // 观察GC操作 public class JavaAPIDemo { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); // 获取实例化对象 System.out.println("【1】TOTAL_MEMORY:" + runtime.totalMemory()); // 获取可用内存 System.out.println("【1】FREE_MEMORY:" + runtime.freeMemory()); // 获取空闲内存 String str = ""; for (int x = 0; x < 3000; x++) { str += x; // 产生垃圾空间 } System.out.println("【2】TOTAL_MEMORY:" + runtime.totalMemory()); // 获取可用内存 System.out.println("【2】FREE_MEMORY:" + runtime.freeMemory()); // 获取空闲内存 runtime.gc(); // 内存释放 System.out.println("【3】TOTAL_MEMORY:" + runtime.totalMemory()); // 获取可用内存 System.out.println("【3】FREE_MEMORY:" + runtime.freeMemory()); // 获取空闲内存 } }
System:System是一个系统类,其最主要的功能是进行信息的打印输出
![]()
Java对象的声明周期
- 创建阶段Creation
- 应用阶段Using
- 不可视阶段Invisible
- 不可到达阶段Unreachable
- 可收集阶段Collected
- 终结阶段Finalized
- 释放阶段Free
// 释放资源 class Member implements Runnable { public Member() { System.out.println("【构造方法】电闪雷鸣,一个优秀的人才诞生了!"); } @Override public void run() { // 清除线程 System.out.println("【对象回收】大家的终点是一样的,一路走好。"); } } class MemberCleaning implements AutoCloseable {// 实现清除的处理 private static final Cleaner cleaner = Cleaner.create() ; // 创建一个清除处理 private Cleaner.Cleanable cleanable ; public MemberCleaning(Member member) { // 注册待清除对象 this.cleanable = cleaner.register(this, member) ; // 注册使用的对象 } @Override public void close() throws Exception {// 自动关闭并释放 this.cleanable.clean(); // 启动清理线程 } } public class JavaAPIDemo { public static void main(String[] args) throws Exception { Member mem = new Member() ;// 实例化对象 System.gc(); // 手工进行GC调用 try (MemberCleaning mc = new MemberCleaning(mem)){ // 中间可以执行一些相关的代码 } catch (Exception e) {} System.out.println("太阳照常升起,地球照样转动,一代更比一代强!");// 不受影响继续执行 } }
大数字:java.math包中提 供了大数字的操作类:BigInteger(整数)、BigDecimal(浮点数),这两个 类都是Number子类
![]()
![]()
![]()
Local:在国际化程序实现的过程之中,对于不同国家的区 域和语言编码,可以通过java.util.Local类的实 例来定义
![]()
ResourceBundle:程序可用来实现已经定义完成的资源文件内容的读取。可以利用类中提供的static方法( getBundle())来实现本类实例化对象的获取。
UUID:Universally Unique Identifier、通用唯 一识别码
ThreadLocal:在多线程并发执行中,可以通过该类实现数据的保存与获取,以此实现准确地对每个线程的数据成员的访问
![]()
![]()
TimerTask:定时调度,可以根据既定的时间安排实现程序任务的自动执行。
![]()
![]()
![]()
Base64:一种直接利用64个可打印字符表示二进制数据的算法
6、io字节流和字符流
IO字节流
字节流定义:用于读/写字节类型的数据(包括ASCII表中的字符)。类可分为InputStream类及其子类及OutputStream类及其子类。
![]()
IO字符流
字符流定义:用于读/写字符(16位 Unicode 码)。包括Reader类及其子类及Writer类及其子类。
![]()
简述字节流与字符流操作的区别
答:
- 字节流没有使用到缓冲区,而字符流使用了;
- 处理各种数据都可以通过字节流完成,而在处理中文的时候使用字符流会更好。
7、JDBC常用几个类
数据库中间件:ODBC, JDBC, ADO.NET
JDBC
![]()
JDBC生命周期
![]()
JDBC常用类
![]()
结合课件掌握各类的使用
8、线程定义及其启动,线程同步
线程定义
继承Thread类
class MyThread extends Thread { // 这就是一个多线程的操作类 private String name ; // 定义类中的属性 public MyThread(String name) { // 定义构造方法 this.name = name ; } @Override public void run() { // 覆写run()方法,作为线程的主操作方法 for (int x = 0 ; x < 200 ; x ++) { System.out.println(this.name + " --> " + x); } } }
实现Runnable接口,其中的run()方法不能返回操作结果
class MyThread implements Runnable { // 定义线程主体类 private String name; // 定义类中的属性 public MyThread(String name) { // 定义构造方法 this.name = name; } @Override public void run() { // 覆写run()方法 for (int x = 0; x < 200; x++) { System.out.println(this.name + " --> " + x); } } }
实现Callable,call()方法可以返回操作结果
import java.util.concurrent.Callable; class MyThread implements Callable<String> {// 多线程主体类 private int ticket = 10; // 卖票 @Override public String call() throws Exception { for (int x = 0; x < 100; x++) { if (this.ticket > 0) {// 还有票可以出售 System.out.println("卖票,ticket = " + this.ticket--); } } return "票已卖光!"; // 返回结果 } }
线程启动
多线程启动的唯一方法就是Thread类中的start()方法:public void start()
- 继承Thread线程的启动
public class TestDemo { // 主类 public static void main(String[] args) { MyThread mt1 = new MyThread("线程A") ;//实例化多线程类对象 MyThread mt2 = new MyThread("线程B") ;//实例化多线程类对象 MyThread mt3 = new MyThread("线程C") ;// 实例化多线程类对象 mt1.start(); // 启动多线程 mt2.start(); // 启动多线程 mt3.start(); // 启动多线程 } } // 执行结果: //线程A --> 0 //线程B --> 0 //线程C --> 0 //线程A --> 1 //线程C --> 1 //...
实现Runnable线程的启动
public class TestDemo { public static void main(String[] args) { MyThread mt1 = new MyThread("线程A") ;//实例化多线程类对象 MyThread mt2 = new MyThread("线程B") ;//实例化多线程类对象 MyThread mt3 = new MyThread("线程C") ;//实例化多线程类对象 new Thread(mt1).start(); //利用Thread启动多线程 new Thread(mt2).start(); // 利用Thread启动多线程 new Thread(mt3).start(); // 利用Thread启动多线程 } }
实现Callable接口线程启动
public class TestDemo { public static void main(String[] args) throws Exception { MyThread mt1 = new MyThread();// 实例化多线程对象 MyThread mt2 = new MyThread();// 实例化多线程对象 FutureTask<String> task1 = new FutureTask<String>(mt1) ; FutureTask<String> task2 = new FutureTask<String>(mt2) ; // FutureTask是Runnable接口子类,所以可以使用Thread类的构造来接收task对象 new Thread(task1).start(); // 启动第一个线程 new Thread(task2).start(); // 启动第二个线程 // 多线程执行完毕后可以取得内容,依靠FutureTask的父接口Future中的get()方法实现 System.out.println("A线程的返回结果:" + task1.get()); System.out.println("B线程的返回结果:" + task2.get()); } }
线程同步
synchronized
![]()
可重入锁(互斥锁):ReentrantLock
![]()
public class ReenTest { private List<Integer> myList; private Lock lock = new ReentrantLock(); public ReenTest(List<Integer> myList){ this.myList=myList; } public Object get(int index){ lock.lock(); try { return myList.get(index); } finally{ lock.unlock(); } } public boolean insert(int newValue){ lock.lock(); try{ return myList.add((Integer)newValue); }finally{ lock.unlock(); } } }
读写锁(读锁:可以由多个线程同时持有;写锁:排它锁):ReadWriteLock
public class ReWrTest { private List<Integer>myList; private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); // 申请一个读写锁 private Lock readLock=rwlock.readLock(); // 建立一个写锁 private Lock writeLock=rwlock.writeLock(); // 建立一个读锁 public ReWrTest(List<Integer> myList){ this.myList=myList; } public Object get(int index){ readLock.lock(); try { return myList.get(index); } finally{ readLock.unlock(); } } public boolean insert(int newValue){ writeLock.lock(); try{ return myList.add((Integer)newValue); }finally{ writeLock.unlock(); } } }
volatile变量
![]()
原子操作(这个方式我也不太熟,暂且写上吧)
public class Counter { private AtomicInteger ia=new AtomicInteger(); public void increase(){ ia.getAndIncrement(); } public int get(){ return ia.get(); } } public class Worker extends Thread{ Counter counter =new Counter(); public Worker(Counter counter) { this.counter=counter; } public void run(){ for(int i=0;i<100;i++){ counter.increase(); } } } public class Index { public static void main(String[] args){ Counter counter =new Counter(); Thread t1 = new Worker(counter); Thread t2 = new Worker(counter); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("counter.get()="+counter.get()); } }
障栅、倒计时门闩、信号量、许可集合、同步队列
9、网络编程典型编程模型
![image-20210117134250762](https://i-blog.csdnimg.cn/blog_migrate/5410d2ef2a4d7a0cb0eb597dca6cb95f.png)
10、html典型标签,form, input ,servlet典型方法,转发,JSP九大对象
html典型标签
单标签:单独出现
<hr> <br> <img>
双标签:成对出现,第一个是开始标签、第二个是结束标签
<html></html> <head></head>
注释标签
显式注释:这种注释方法只能在<%%> 外面,否则无效,所谓显示注释,是指在客户端可以看见
<!--注释内容-->
隐式注释
- //注释内容 单行注释
这种注释方法只能在<%%> 里面,否则无效
所谓隐式注释,是指在客户端看不见,但在服务器端可以看见
- /注释内容/ 多行注释
这种注释方法只能在<%%> 里面,否则无效
所谓隐式注释,是指在客户端看不见,但在服务器端可以看见
- <%--注释内容--%>
这种注释方法只能在<%%> 外面,否则无效
所谓隐式注释,是指在客户端看不见,但在服务器端可以看见
超链接标签:链接的地址 =属性Href的值
<!未使用属性> <a> </a> <!使用属性> <a href=“http://www.baidu.com/”> </a>
表格标签:表格填充色 =属性bgcolor的值
<!未使用属性> <table> </table> <!使用属性> <table bgcolor=“red”> </table>
![]()
<!未使用属性>
```
![image-20210117181259000](https://i-blog.csdnimg.cn/blog_migrate/1b89cbd70691e1cc17c7528d06b895ac.png)
Servlet典型方法
init() 方法
- 在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。 可以配置服务器,以便在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init() 。
- 缺省的 init() 方法通常是符合要求的,但也可以用定制 init() 方法来覆盖它,典型的是管理服务器端资源。
service() 方法
- service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。 在 HttpServlet 中已存在 service() 方法。缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。
doGet() 方法
- 当一个客户通过 HTML 表单发出一个 HTTP GET 请求或直接请求一个 URL 时,doGet() 方法被调用。与 GET 请求相关的参数添加到 URL 的后面,并与这个请求一起发送。当不会修改服务器端的数据时,应该使用 doGet() 方法。
doPost() 方法
- 当一个客户通过 HTML 表单发出一个 HTTP POST 请求时,doPost() 方法被调用。与 POST 请求相关的参数作为一个单独的 HTTP 请求从浏览器发送到服务器。当需要修改服务器端的数据时,应该使用 doPost() 方法。
destroy() 方法
- destroy() 方法仅执行一次,即在服务器停止且卸装 Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。
getServletConfig() 方法
- getServletConfig() 方法返回一个 ServletConfig 对象,该对象用来返回初始化参数和 ServletContext。ServletContext 接口提供有关 servlet 的环境信息。
getServletInfo() 方法
- getServletInfo() 方法是一个可选的方法,它提供有关 servlet 的信息,如作者、版本、版权。
- 当服务器调用 sevlet 的 service()、doGet() 和 doPost() 这三个方法时,均需要 “请求”和“响应”对象作为参数。“请求”对象提供有关请求的信息,而“响应”对象提供了一个将响应信息返回给浏览器的一个通信途径。
![]()
JSP九大内置对象及其作用域
Web应用中,JSP创建的对象有一定的生命周期,也有可能被其他组件或者对象访问。对象的生命周期和可访问性称为作用域。四种范围对象作用域从小到大顺序如下:page----request----session----application
- page:页面域,作用域仅限于当前页面对象,可以近似于理解为java的this对象,离开当前JSP页面(无论是redirect还是forward),则pageContext中的所有属性值就会丢失。
- request:请求域,作用域是同一个请求之内,在页面跳转时,如果通过forward方式跳转,则forward目标页面仍然可以拿到request中的属性值。如果通过redirect方式进行页面跳转,由于redirect相当于重新发出的请求,此种场景下,request中的属性值会丢失。
- session:会话域,session的作用域是在一个会话的生命周期内,会话失效,则session中的数据也随之丢失。
- application:应用域,作用域是最大的,只要服务器不停止,则application对象就一直存在,并且为所有会话所共享。
- session对象:当客户第一次访问Web服务器发布的目录是,Web服务器会自动创建一个session对象并分配一个ID号。作用域Session。
- application对象:用户获得和当前Web应用程序相关的信息。由PageContext类中的getServletContext方法返回。可用于保存和获得全局对象(使用setAttribute和getAttribute方法)。作用域Application。
- request对象:主要用于取得客户在表单中提交的数据信息及多个页面之间的数据传递,也可以取得Web服务器的参数,它与servlet中的request对象是对应的。作用域Request。
- response对象:主要是响应客户的请求以及向客户端输出请求的处理结果,与servlet中的response对象相对应。作用域Page。
- out对象:JSP中最常用的内置对象,主要向客户端输出响应信息。作用域Page。
- exception对象:只有page指令的isErrorPage属性的值为true时(<%@ page isErrorPage = "true"%>)JSP中的exception对象才有效。当JSP页面在执行过程中发生错误时,就可以使用exception对象得到相应的错误信息。作用域Page。
- config对象:在JSP页面中使用config对象可以或得web.xml文件中于当前JSP页面相关的配置信息,如参数、Servlet名等。作用域Page。
- pageContenxt对象:是javax.servlet.jsp.pageContext类的实例(该类封装了以上几个内置对象)。当JSP页面调用普通Java类时可以将pageContext对象当作参数传入相应的方法中。如此就可通过该对象来获取其他内置对象。作用域Page。
- Page对象:页面对象,类型为:javax.lang.Object。作用域Page。
简答题:(20分)
IOC容器
![image-20210118130602158](https://i-blog.csdnimg.cn/blog_migrate/ed43e34249374c0277c750d5fffb7e39.png)
静态代理
![image-20210117125143022](https://i-blog.csdnimg.cn/blog_migrate/64f35a89af2b82810070b33624ad247e.png)
![image-20210117102240378](https://i-blog.csdnimg.cn/blog_migrate/5042d585020e809f45564e0f81a0c1f8.png)
例子实现:
//创建接口
public interface Seakable {
public void speak(String message);
}
// 创建实际类
public class Person implements Speakable {
@Override
public void speak(String message) {
System.out.println("Speak: " + message);
}
}
// 创建代理
public class PersonProxy implements Speakable {
private Person person; // 被代理的实际类
public PersonProxy(Person person) {
this.person = person;
}
@Override
public void speak(String message) {
this.person.speak(message); // 代理类调用实际类处理信息
}
}
// 测试类
public class Bootsrap {
public static main(String[] args) {
Person person = new Person();
PersonProxy proxy = new PersonProxy(person);
proxy.speak("Lesson one!");
}
}
// 输出结果为:
Speak:lesson one!
工厂模式
![image-20210117124807271](https://i-blog.csdnimg.cn/blog_migrate/fa06243992b19bf92ddbc7ccf0514448.png)
例子和代码实现:
![image-20210117124935817](https://i-blog.csdnimg.cn/blog_migrate/c237e79579573e591c4600ac23b9212b.png)
// 接口 Fruit源码
public interface Fruit {
// 生长
void grow();
// 收获
void harvest();
// 种植
void plant();
}
// Apple类
public class Apple implements Fruit {
private int treeAge; // 自己的属性
// 实现接口的方法
public void plant() {
log("Apple has been plant!");
}
public void grow() {
log("Apple is growing···");
}
public void harvest() {
log("Apple has been harvested!");
}
}
// 其他水果类和Apple类似实现
// FruitGardener类
public class FruitGardener {
public static Fruit factory(String which) throw BadFruitException {
if (which.equalsIgnoreCase("apple")) {
return new Apple();
}else if (which.equalsIgnoreCase("strawberry")) {
return new Strawberry();
}else if (which.equalsIgnoreCase("grape")) {
return new Grape();
}else {
throw new BadFruitException("Bad fruit request")
}
}
}
Servlet生命周期图
![image-20210117125538390](https://i-blog.csdnimg.cn/blog_migrate/13b7929bdc3f1fce43688af8c1542fa6.png)
网络编程模型图
![image-20210117134250762](https://i-blog.csdnimg.cn/blog_migrate/87ca93f1e52ad3913c3b4e8bf2448465.png)
MVC架构图
![image-20210118160241088](https://i-blog.csdnimg.cn/blog_migrate/93933afbf4c01cecad9c103316e41b28.png)
![image-20210118161733711](https://i-blog.csdnimg.cn/blog_migrate/b45845830963d66808d42d00a2b4fd65.png)
![image-20210118161908980](https://i-blog.csdnimg.cn/blog_migrate/ff55bd7ba7a118e52c371d5de80668f5.png)
![image-20210118162012891](https://i-blog.csdnimg.cn/blog_migrate/2a7e8861c8275242a87c0fbbff3e0e66.png)
![image-20210117135121008](https://i-blog.csdnimg.cn/blog_migrate/c153fe1749a76c1c687febe737fe273b.png)
Servlet时序图
![image-20210117125611370](https://i-blog.csdnimg.cn/blog_migrate/abc75e9e2a92b2a15e80d3d05f5100e4.png)
分层领域模型规约(阿里巴巴技术手册),
- DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
- DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
- BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
- Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
- VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
- AO (Application Object):应用对象,在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
web.xml文件中关于servlet的配置
![image-20210118170216862](https://i-blog.csdnimg.cn/blog_migrate/3598303f3fb92e1b538c8528073d7dea.png)
![image-20210118170231852](https://i-blog.csdnimg.cn/blog_migrate/36b6e1f461dc2808ac2249c285d31440.png)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>servlet2</display-name>
<servlet>
<servlet-name>configDemoServlet</servlet-name>
<servlet-class>com.demo.ConfigDemoServlet</servlet-class>
<init-param>
<param-name>email</param-name>
<param-value>hacker@163.com</param-value>
</init-param>
<init-param>
<param-name>telephone</param-name>
<param-value>8899123</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>configDemoServlet</servlet-name>
<url-pattern>/configDemo.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ServerInfo</servlet-name>
<servlet-class>com.demo.ServerInfo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServerInfo</servlet-name>
<url-pattern>/ServerInfo</url-pattern>
</servlet-mapping>
</web-app>
编程题目:(30分)
多线程求最大值
package main.experiment4.multithreadingforarraymaximum;
import javax.print.attribute.standard.PrinterMessageFromOperator;
import java.util.concurrent.locks.Lock;
/**
* @author LiuXingWu
* @create 2020-12-20 13:16
*/
public class GetMaxNum {
static Double AllMax = Double.MIN_VALUE; // 最大值
public static void main(String[] args) throws InterruptedException {
int numOfdata = 10;
int numOfThread = 3;
int tempSize = numOfdata / numOfThread;
// System.out.println(tempSize);
Thread[] threads = new Thread[numOfThread]; // 线程数组
double[] testData = new double[numOfdata]; // 测试数据
double[][] threadData = new double[numOfThread][tempSize]; // 每个线程处理的数据
if (tempSize * numOfThread < numOfdata) {
threadData = new double[numOfThread][tempSize + 1];
}
// 随机生成数据
for (int i = 0; i < numOfdata; i++) {
testData[i] = Math.random() * 100 + 1; // 生成1~100之间的随机数
}
System.out.print("测试数据为:");
for (double d : testData) {
System.out.printf("%.2f, ", d);
}
// 数据划分
int temp = 0;
for (int i = 0; i < numOfThread; i++) {
for (int j = 0; j < threadData[0].length; j++) {
if (temp < testData.length) {
threadData[i][j] = testData[temp];
temp++;
}
}
}
// 创建线程
for (int i = 0; i < numOfThread; i++) {
threads[i] = new workThread(threadData[i]);
}
// 启动线程
for (int i = 0; i < numOfThread; i++) {
threads[i].start();
}
// 阻塞线程
for (int i = 0; i < numOfThread; i++) {
threads[i].join();
}
System.out.printf("\n最大值为:%.2f", AllMax);
}
public static class workThread extends Thread {
private double[] dataArray;
public workThread(double[] dataArray) {
this.dataArray = dataArray;
}
@Override
public void run() {
for (double d : dataArray) {
if (d > AllMax) {
AllMax = d;
}
// synchronized (AllMax) {
// if (d > AllMax) {
// AllMax = d;
// }
// }
}
}
}
}
多线程排序
package fuxi;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
/**
* 双线程对数组进行排序
*/
public class ThreadSort {
public static void main(String[] args) throws InterruptedException {
int[] arr = {1, 4, 2, 12, 35, 23, 21, 12, 32, 24};
int[] x = new int[5];
int[] y = new int[5];
//将原始数组分为两组数组,0-4 , 5-9
System.arraycopy(arr, 0, x, 0, 5);
System.arraycopy(arr, 5, y, 0, 5);
//两个线程,计数器为2,开始计数
CountDownLatch count = new CountDownLatch(2);
//定义并开启线程,二线程分别开始排序并计数
Ts t1 = new Ts(x, count);
Ts t2 = new Ts(y, count);
t1.start();
t2.start();
//阻塞线程,等待所有线程都完成排序任务
count.await();
//排序合并两个有序数组
int[] res = new ThreadSort().merge(x,y);
//打印排序结果
for (int i = 0; i < res.length; i++) System.out.print(res[i]+" ");
}
/**
* 合并排序合并两个有序数组
* @param x
* @param y
* @return
*/
int[] merge(int[] x, int[] y){
int[] arr = new int[x.length + y.length];
int i=0, px=0, py=0;
while (px < x.length && py<y.length){
if(x[px] < y[py]) arr[i++] = x[px++];
else arr[i++] = y[py++];
}
while (px < x.length) arr[i++] = x[px++];
while (py < y.length) arr[i++] = y[py++];
return arr;
}
}
/**
* 线程类
* 用CountDownLatch来控制两个线程的同步
*/
class Ts extends Thread{
private int[] array;
private CountDownLatch count;
public Ts(int[] array, CountDownLatch count){
this.array = array;
this.count = count;
}
public void run(){
Arrays.sort(array);
count.countDown();
}
}
JSP+Servlet+JDBC
参考实验8示例代码
// 简单JDBC模板,来自同学许九九的代码
package utils;
import java.sql.*;
/**
* @CLassName : JdbcUtils
* @Description : TODO
* @Author : 九九
* @Date : 2021/1/18,15:51
**/
public class JdbcUtils {
private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/book";
private static String username = "root";
private static String password = "root";
private static Connection connection = null;
private static PreparedStatement preparedStatement = null;
private static ResultSet resultSet = null;
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void getConnection(){
try {
connection = DriverManager.getConnection(url,username,password);
System.out.println(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static int update(String sql,Object... args){
getConnection();
int executeUpdate = 0;
try {
preparedStatement = connection.prepareStatement(sql);
if (args != null){
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i+1, args[i]);
}
}
executeUpdate = preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
close();
return executeUpdate;
}
}
public static ResultSet query(String sql,Object... args){
getConnection();
try {
preparedStatement = connection.prepareStatement(sql);
if (args != null){
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i+1,args[i]);
}
}
resultSet = preparedStatement.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}finally {
return resultSet;
}
}
public static void close(){
try {
if (preparedStatement != null){
preparedStatement.close();
}
if (resultSet != null){
resultSet.close();
}
if (connection != null){
connection.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Web服务器
![image-20210118150612485](https://i-blog.csdnimg.cn/blog_migrate/ab6c9a8481ee571d37934e05bc4d8cb2.png)
package fuxi;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 简易Web服务器
* 用户可以通过浏览器来访问服务器,服务器可以接收用户的请求,并返回处理结果
*/
public class MyWebServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8010); //设置服务器及其监听端口,没有指明IP地址,默认情况下通过服务端的所有IP地址均可访问
Socket socket = server.accept(); //等待客户端连接,如果有用户访问则分配一个socket连接给用户
InputStream input = socket.getInputStream();//客户端输入流
byte[] bytes = new byte[1024];
input.read(bytes);
System.out.println(new String(bytes));
PrintWriter writer = new PrintWriter(socket.getOutputStream()); //客户端输出流
writer.println("HTTP/1.0 200 OK \r\n" +"ContentType: text/html " +
"\r\n\r\n" + "<h1>Hello World!</h1>"); // 将结果打印在屏幕上
writer.flush();
socket.close();
}
}