git使用手册

关于版本控制

版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。

如果你是位图形或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版 本。采用版本控制系统(VCS)是个明智的选择。有了它你就可以将某个文件回溯到之前的 状态,甚至将整个项目都回退到过去某个时间点的状态。你可以比较文件的变化细节,查 出是谁最后修改了什么地方从而造成某些怪异问题,又是谁在何时报告了某个功能缺陷, 等等。使用版本控制系统通常还意味着,就算你胡来搞砸了整个项目,把文件改的改,删的删,你也可以轻松恢复到原先的样子。而由此额外增加的工作量却微乎其微。

1 本地版本控制系统

大多本地版本控制系统都是采用某种简单的数据库来记录文件的历次更新差异。如下图:

本地版本控制

2 集中化的版本控制系统

这类系统,诸如 CVS,Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或 者提交更新。如下图:

集中化版本控制

3 分布式版本控制系统

在这类系统中,诸如 Git,Mercurial,Bazaar 还有 Darcs 等,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份。如下图:

分布式版本控制

安装Git

有许多安装方式,概况起来主要有两个,一种是通过编译源代码来安装;另一种是使用为特定平台预编译好的安装包。

  1. 在Linux上安装

    如果要在Linux上安装预编译好的git二进制安装包,可以直接使用系统提供的包管理工具。在Fedora上yum安装:

    1
    $ yum install git-core

​ 在Ubuntu这类Debian体系的系统上,可以用apt-get安装:

1
$ apt-get install git-core
  1. 在Mac上安装

    在Mac上安装git有两种方式。最容易的当属使用图形化的git安装工具,下载地址:

    下载地址

    另一种是通过Macports(http://www.macports.org)安装。安装好Macports之后,使用下面的命令安装git:

    1
    $ sudo port install git-core +svn +doc +bash_competition +gitweb
  2. 在Windows上安装

    • 有个叫做msysgit的项目提供了安装包,可以从Google code的页面上下载安装文件:

    下载地址

    安装完之后,就可以使用命令行的git工具了,另外还有一个图形界面的git项目管理工具。

常用git命令

  1. 用户信息配置

    要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次git提交都会引用这两条信息,说明是谁提交了更新。

    1
    2
    $ git config --global user.name ""
    $ git config --global user.email ...
  2. 设置的是默认使用的文本编辑器

    默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。也可以重新设置:

    1
    $ git config --global core.editor ...
  3. 获取帮助说明指令

    1
    git help [子命令]
  4. 初始化本地仓库

    使用以下命令会生成一个.git文件夹(本地代码仓库),.git文件夹用来管理本地代码,修改上传,版本回退。

    1
    git init
  5. 查看配置信息

    查看信息是否配置成功

    1
    git config -l
  6. 将创建的文件添加到stage暂存区

    1
    git add .
  7. 提交文件到指定的分支(默认mater分支)

    1
    git commit -m "(添加注释说明)"
  8. 删除文件并提交

    1
    git rm 文件名
  9. 将删除的文件提交到本地库

    1
    git commit -m "(添加注释说明)"
  10. 查看日志

    查看git的更新日志和40位哈希值版本号

    1
    git log

    查看git的更新日志和7位哈希值版本号

    1
    git relog
  11. 代码版本回退

    1
    2
    3
    4
    重置到当前版本 git reset --hard HEAD
    重置到上上个版本 git reset --hard HEAD^^
    重置到往上3个版本 git reset --hard HEAD~3
    重置到指定版本 git reset --hard 七位版本号
  12. 恢复

    1
    2
    恢复某次提交的状态 git revert <$id>
    恢复最后一次提交的状态 git revert HEAD
  13. 查看、切换、创建、删除分支

    • 查看分支

      查看远程分支 git branch -r

      查看各个分支最后提交的信息 git branch -v

      查看已经被合并到当前分支的分支 git branch --merged

      查看尚未被合并到当前分支的分支 git branch --no --merged

    • 切换分支

      git checkout <branch>

    • 创建新分支

      创建新的分支并且切换过去 git checkout -b <new_branch>

      创建基于branch分支的新分支new_branch git checkout-b <new_branch> <branch>

      把某次历史记录checkout出来并创建新分支 git checkout $id -b <new_branch>

    • 删除分支

      删除某个分支 git branch -d <branch>

      强制删除某个分支(未被合并的分支在被删除时需要强制) git branch -D <branch>

  14. git暂存管理

    暂存 git stash

    列所有暂存区 git stash list

    恢复暂存区的内容 git stash apply

    删除暂存区 git stash drop

  15. git远程仓库管理

    查看远程服务器地址 git remote -v

    查看远程服务器仓库状态 git remote show origin

    添加远程仓库地址 git remote add origin ...

    删除远程仓库 git remote rm <repository>

  16. git远程分支管理

    • 远程分支抓取

      抓取远程仓库所有分支更新并合并到本地 git pull

      抓取远程仓库所有分支更新并合并到本地,不要快进合并 git pull --no--ff

    • push远程分支

      push所有分支 git push

      将本地分支推到远程主分支 git push origin master

      将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库) git push -u origin master

      创建远程分支 git push origin <local_branch>
      创建远程分支 git push origin <local_branch>:<remote_branch>
      先删除本地分支,然后再push删除远程分支 git push origin :<remote_branch>

    Git与GitHub

    ​ GitHub是目前为止最大的开源Git托管服务,并且是少数同时提供公共托管和私人托管服务的站点之一。

    接下来的内容主要描述讲解如何使用git来将本地仓库与GitHub账户的仓库建立连接。

  17. 建立本地git仓库

    进入你要同步的本地项目的根目录下,执行git命令:

    git init

  18. 将项目中所有的文件添加到仓库中

    git add .

    如果只想添加某个特定的文件,只需把 . 换成特定文件名即可。

  19. 将add进来的文件提交到仓库

    git commit -m "(添加注释语句 )"

  20. 在自己GitHub上创建一个仓库(repository),界面如下:

    创建仓库

    Repository name:填写仓库名称

    Description:描述,填写仓库的主题、内容

    Public、Private:仓库类型可选:公有、私有

    Initialize this repository with a README:初始化readme说明文件

    最后,点击Create repository就可以成功创建仓库了。

  21. 获取仓库地址

    要想本地仓库与远程建立连接,需要有远程仓库的地址,获取方法如下图:

    获取仓库地址

  22. 将本地仓库关联到GitHub上

    进入本地仓库目录,执行如下git命令:

    git remote add origin ...(远程仓库地址)

  23. 在上传本地目录之前,要先pull一下,执行如下git命令:

    git pull origin master

  24. 上传本地仓库到GitHub远程仓库

    git push -u origin master

  25. 执行完成,如果没有异常,等待上传完成就上传成功。期间会要求输入账户名和密码,只需要输入GitHub的账户和密码就行。

Git GUI

Git有提供图形界面,下面简要介绍如何使用。

  • 打开Git GUI,在右下角处选择”重新扫描”,如下:

    扫描新的改动

  • 扫描完成后,在左上角处会显示”未缓存的改动”,如下:(点击左边的图标即可将文件添加到已缓存区)

    未缓存

  • 点击左侧图标,将”未缓存文件”添加到”已缓存文件”,如下:

    已缓存

  • 在”提交描述”框内填写此次修改的目的和原因,然后提交。

    第一行:一句话概况你做的修改。

    第二行:空行。

    第三行:请描述为什么你做的这些改动是好的。

    提交描述

  • 最后提交,会要求输入用户名和密码,同样是GitHub的账户名和密码即可。

    账户

    密码

  • 上传成功,提示如下:

    上传成功

大概流程差不多这样了,还有其他一些功能,比如分支管理,等深入学习了有时间再分享。

后言

感谢阅读,欢迎指教。本人简书和GitHub —>(简书/GitHub

good luck!

春招Android岗实习面试分享

前言

感觉时间过得很快,学期又过半了,红红火火的春招实习也基本拉下帷幕。 跟许多同学一样我也去尝试了,摸着石头过河磕磕碰碰,最终确定了大疆dji的offer,很感谢无论是在技术上还是面试经验上给过建议和帮助的大佬们。技术岗考核的标配是:笔试+2轮技术面+HR面,等待的过程挺煎熬的,因为你不清楚自己会在哪一轮被KO掉,所以还得边复习边等待结果。想说把面试经验总结分享一下,相信这种东西准备面试的同学已经司空见惯了(我是学Android方向的),欢迎探讨哈。

简历篇

简历制作技巧的作用不可忽视,毕竟很多大公司会筛选简历然后再给出笔试机会,要尽量在简历内容中体现出自己的优势和实力(比如说项目经验、掌握技能)。我做的第一份简历详细地叙述了一些项目细节,导致整体排版感觉密密麻麻,难以get points。后来在大佬指导下做了修改,只列举出了主要实现的功能和技术要点。技术岗的同学有个人博客可以附上博客地址。

关于简历制作技巧,最近班主任在课堂传授了一些干货(一原则九要点),下面跟大家分享一下:

  • 一原则是符合岗位需求

  • 要点1:黄金位置利用好(比如简历不止一页的,重要的信息最好放在第一页)

  • 要点2:不要无用信息(比如简历所付照片正常人能辨别出性别的,那么个人信息就可不添加‘性别’属性了)

  • 要点3:注重简历的诚实度和表达(假的东西就不要写啦,表达要简明扼要)

  • 要点4:版面布局要进行逻辑分类

  • 要点5:要冷冷的数字与细节,不要火热的抒情描述

  • 要点6:好简历=过程+结果(比如参赛担任的角色和主要的职责,最终有获奖的话也要写上)

  • 要点7:重要信息上下留白

  • 要点8:本专科简历,一般不超过1页

  • 要点9:法无定法,凸显特色

    面试Android岗需要具备的技能

Android是基于Linux的移动操作系统,面试Android开发岗,要掌握的知识除去Android本身的开发技能外,计算机相关基础学科也是考核的范畴。(有些要点后面附有参考链接,想了解相关知识的老铁可以点击进入)

计算机网络篇

面tx的时候面试官就问了很多网络安全传输细节,一起去的同学也反映网络知识问的挺多。下面列举一些常问的问题:

  • TCP/UDP的区别 (参考
  • TCP三次握手和四次挥手过程,为什么是三次握手,为什么要四次挥手而不是三次挥手就行?
  • TCP是如何做到拥塞控制的?(参考
  • 应用推送与服务器建立连接的方式有哪些,如何做到传输安全?(参考1/参考2
  • HTTPS建立连接的流程,证书有什么用,怎么解决中间人劫持攻击?(参考

操作系统篇

操作系统能帮我们更好地理解多线程编程的原理,主要问题还是集中在线程与进程的区别与联系(参考):

  • 线程与进程的区别
  • 进程的三种基本状态及其转换
  • 产生死锁的必要条件和预防死锁的方法
  • 银行家算法的数据结构、银行家算法算法流程、安全性算法的算法流程
  • 进程同步的生产者-消费者问题

数据结构和算法

算法工程师岗和c/c++开发工程师对算法要求会高些,移动端要掌握基本数据结构和算法(比如栈、队列、树、八大基本排序算法)。不过有一些公司对产品的效率要求比较高,像WPS的面试官一上来就搞个算法题让写代码思路(输入一字符串,要求排序输出其中的单词以及单词出现的次数。),我用hashmap实现的,完了他就把问题转向如何优化这个算法,结合着问Java集合类框架。

  • 基本排序算法(冒泡、插入、快排、归并、堆排)的伪代码和时间复杂度分析(参考
  • 单向链表和双向链表的结点增删操作
  • 栈和队列的区别和实现
  • 查找算法
  • 用贪心算法、动态规划思想解决实际问题
  • 二叉树递归遍历的三种方式,给出其中的两种遍历输出结果求第三种遍历输出结果

Java篇

Android开发有很大部分用到Java语言,所以熟练掌握Java知识是必不可少的。最基本的面向对象概念、主要类框架(如集合类、IO类)、多线程操作、GUI编程等都得掌握,基础扎实之后可以考虑看些进阶的书籍,比如《Java编程思想》、《effective Java》等。

  • 面向对象的三大特性及其理解,多态如何体现
  • Java集合类框架,Set、List、Queue的区别和应用场景(参考
  • Java类加载器(参考
  • 什么是线程安全
  • Java多线程实现方式
  • Java多线程同步方式
  • HashMap与Hashtable的区别,HashMap怎样实现线程安全、HashMap源码(参考
  • String、StringBuffer、StringBuilder的区别及各自应用场景
  • Java反射机制
  • JVM四大引用

设计模式以及项目篇

基础面过后,二面问得较多的是设计模式和项目相关的问题,面了几个公司都问到了设计模式,比如单例模式、观察者模式、MVP模式等,设计模式推荐《Head First设计模式》这本书。至于项目,主要问职责和技术要点(按简历上写的内容展开),实现过程中遇到过的困难,怎么分配工作,往外了说可能会让你想想优化方案,或者说重做一次项目的话自己会想优化哪些地方。

Android篇

主角终于闪亮登场啦,这是面试的重点区域, 考察内容也比较广泛,从四大组件到流行框架和封装库,都可能涉及,很多时候也是结合着项目来讲。

  • Android四大组件的理解
  • Activity的四种启动方式、Activity的生命周期
  • Handler事件分发机制(理清Handler、Looper、Message、MessageQueue之间的关系)(参考
  • 静态广播和动态广播的区别
  • 三层图片缓存加载机制
  • Activity与Service的通信方式
  • Android animation的几种方式(参考
  • 对象序列化相关知识
  • AsyncTask的机制、源码(参考
  • view的绘画机制
  • Android数据持久化技术
  • Android使用http协议访问网络的两个方式(HttpURLConnection、HttpClient)
  • 项目中使用过的第三方库
  • 内存泄漏、内存溢出的区别,内存泄露如何检测

准备篇

机会总是留给有准备的人。面试前最好得到网上搜一搜应聘岗位的面经,看看常问面试题。每次面试过后暴露出来的问题要及时解决,找博客或者书籍学习相关知识,做个总结,争取下次面试能较全面地回答同个问题。当然,有时间的话也可以了解一下应聘公司的文化和相关产品,聊到对公司了解的时候就有话谈,显得也更有诚意。

技术面通过之后会有个HR面,一般来说不会为难你的。可能会问家庭情况、兴趣爱好、职业规划、处事方法,大学最难忘的回忆或者是最骄傲的事情,为什么会选这个岗位and so on。

大概就讲到这啦,感谢阅读,欢迎指教。本人简书和GitHub —>(简书/GitHub

good luck!

interview experience one

前言

大三下学期,正值春招之际,大家伙都在尝试着找实习了。我也不例外,也在投递简历寻求面试机会,前两天去了”广州久邦数码”面试Android开发实习生。初次参加技术面试,没有什么经验,虽说先前有在网上看一些面试经验和宝典,但是面试官一问起问题来,会有种似曾相识的感觉,回答起来模模糊糊,不够彻底,仔细一想应该是自己掌握程度不够,还得好好学习。给个忠告,面试的时候态度一定要认真,不然很容易紧张,造成答题思路不清晰,会影响后面的回答。

下面我就此次面试题目以及答案做个总结,欢迎大家共同探讨。

Android

-Listview如何优化?

对于Android开发者来说,listview是一个熟客了。它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示。列表数据的显示需要4个元素,分别为:

(1)用来展示列表的Listview;

(2)用来把数据映射到Listview上的Adapter;

(3)需要展示的数据集;

(4)数据展示的View模板。

Listview控件只负责加载、管理视图(每项数据称为Item View)。至于有多少数据项、每一项数据是如何显示的它并不关心,这一 切交由adapter类去实现,通过adapter模式,用户只需要重写几个函数就可以将listview的每项数据构建出来。需要重写的函数有:

(1)getCount()函数:获取数据的个数;

(2)getItem(int )函数:获取position位置的数据;

(3)getItemId(int )函数:获取position位置的数据Id,一般直接返回position即可;

(4)getView(int View,ViewGroup)函数:获取position位置上的Item View视图。

每个Item View是通过getView()函数实现,在这个函数中用户必须构建Item View,然后将该position位置上的数据绑定到Item View上。这样一来,数据和视图就绑定在一起了。

但是,重点来了。并不是有多少数据项就会生成多少Item View,Android采用了视图复用的形式来避免创建过多的Item View,这样能够非常有效地提升性能和降低内存占用率。在处理数据量较大时,listview会构建铺满屏幕所需的Item View个数,当屏幕向下滚动时,第一项数据就会滚出屏幕的可见范围之内,并且进入listview的一个recycler中,recycler会将该视图缓存。此时要加载新插入的视图,listview会先从recycler中获取视图,如果视图存在,那么用户可以直接使用该缓存视图,否则才重新创建视图。也就是会有视图复用判断逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
public View getView(int position,View covertView,ViewGroup parent){
View view = null;
// 有视图缓存,则复用视图
if(covertView != null){
view = covertView;
}else{
// 重新加载视图
}
// 进行数据绑定
// 返回Item View
return View;
}

总的来说,listview就是通过adapter模式、观察者模式、Item View复用机制实现了高效的列表显示。

顺便提一下GridView,它与listview非常相似,同样继承自AbsListView,不同的是布局方式,GridView通过网格布局形式展示。本是同根生的listview和GridView有很好的的兼容性,同一个adapter可以设置给listview或者GridView,不需要半点修改。当然也可以同时设置给这两个视图,这样一来,两个视图都作为该adapter的观察者。

-XML格式数据、JSON格式数据如何解析?

数据要以什么样的格式在网络上传输呢?随便传递一段文本肯定是不行的,因为另一方不知道这段文本的用途是什么。因此,一般我们会在网络上传输一些格式化后的数据,这种数据会有一定的结构规格和语义,当另一方收到数据消息之后就可以按照相同的结构规格进行解析,从而取出他想要的那部分内容。在网络上传输数据最常用的格式有两种:XML和JSON。

XML格式数据解析较常用的有两种:pull解析和sax解析。

pull解析(结合eg来讲):

通过URL参数结合http协议访问服务器,得到服务器返回的数据。接下来,要获取到一个XmlPullParserFactory的实例,并借助这个实例得到XmlPullParser对象,然后调用XmlPullParser的setInput()方法将服务器返回的XML数据设置进去就可以开始解析了。解析的过程也非常简单,通过getEventType()可以得到当前的解析事件,然后在一个while循环中不断地进行解析,如果当前的解析事件不等于XMLPullParser.END_DOCUMENT,说明解析工作还没完成,调用next()方法后可以获取下一个解析事件。

在while循环中,我们通过getName()方法得到当前结点的名字,如果发现结点名等于id、name或version,就调用nextText()方法来获取结点内具体的内容,每当解析完一个app结点后就将获取到的内容打印出来:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class MainActivity extends Activity implements OnClickListener {
……
private void sendRequestWithHttpClient() {
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpClient httpClient = new DefaultHttpClient();
// 指定访问的服务器地址是电脑本机
HttpGet httpGet = new HttpGet("http://10.0.2.2/
get_data.json");
HttpResponse httpResponse = httpClient.execute(httpGet); if(httpResponse.getStatusLine().getStatusCode()==200){
// 请求和响应都成功了
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity,
"utf-8");
parseJSONWithJSONObject(response);
}catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private void parseXMLWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
// 开始解析某个结点
case XmlPullParser.START_TAG: {
if ("id".equals(nodeName)) {
id = xmlPullParser.nextText();
} else if ("name".equals(nodeName)) {
name = xmlPullParser.nextText();
} else if ("version".equals(nodeName)) {
version = xmlPullParser.nextText();
} break;
}
// 完成解析某个结点
case XmlPullParser.END_TAG: {
if ("app".equals(nodeName)) {
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "version is " + version);
}
break;
}
default: break;
}
eventType = xmlPullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

sax解析:也是一种常用的解析方式,虽然它的用法比pull解析更复杂一些,但在语义方面会跟更加的清楚。

通常情况下我们会新建一个类继承自Defaulthandler,并重写父类的5个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyHandler extends DefaultHandler {
@Override
public void startDocument() throws SAXException {
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
}
@Override
public void endElement(Stringuri,StringlocalName,StringqName)throws SAXException {
}
@Override
public void endDocument() throws SAXException {
}
}

startDocument()方法会在开始XML解析的时候调用,startElement()方法会在开始解析某个结点的时候调用,characters()方法会在获取结点中内容的时候调用,endElement()方法会在完成解析某个结点的时候调用,endDocument()方法会在完成整个XML解析的时候调用。其中,startElement()、characters()、endElement()这三个方法是有参数的,从XML解析出的数据就会以参数的形式传入到这些方法中。需要注意的是,在获取结点中的内容时,characters()方法可能会被调用多次。

在得到了服务器返回的数据后,我们这次去调用 parseXMLWithSAX()方法(自定义的)来解析 XML 数据。parseXMLWithSAX()方法中先是创建了一个 SAXParserFactory的对象,然后再获取到 XMLReader对象,接着将我们编写的 ContentHandler的实例设置到 XMLReader中,最后调 用 parse()方法开始执行解析就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void parseXMLWithSAX(String xmlData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
// 将ContentHandler(一个继承自Defaulthandler的类)的实例设置到XMLReader中
xmlReader.setContentHandler(handler);
// 开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));
} catch (Exception e) {
e.printStackTrace();
}
}
}

JSON格式数据解析较常用的有两种:使用JSONObject、使用GSON。

比起XML,JSON的主要优势在于它的体积更小,在网络上传输的时候可以更省流量。但缺点在于,它的语义性较差,看起来不如XML直观。

JSONObject:得到服务器返回的数据后,我们可以通过getJSONObject(int position)方法来获取第i个JSON对象,然后再对应地取出字段内容。eg如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
假设文件中的JSON格式数据如下:
[{"id":"5","version":"5.5","name":"Angry Birds"},
{"id":"6","version":"7.0","name":"Clash of Clans"},
{"id":"7","version":"3.5","name":"Hey Day"}]
……
private void parseJSONWithJSONObject(String jsonData) {
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String version = jsonObject.getString("version");
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "version is " + version);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

GSON:谷歌提供的开源库可以让解析JSON数据的工作简单到让你不敢想象的地步,不过要使用此功能的话必须在项目中添加一个GSON的Jar包。。首先我们需要将GSON的资源压缩包下载下来, 下载地址是:http://code.google.com/p/google-gson/downloads/list。 其中 gson-2.2.4.jar这个文件就是我们所需要的了,现在将它拷贝到项目的 libs目录下,GSON库就会自动添加到 项目中了。

那么GSON库究竟是神奇在哪里呢?其实它主要就是可以将一段JSON格式的字符串自 动映射成一个对象,从而不需要我们再手动去编写代码进行解析了。

比如说一段 JSON格式的数据如下所示:

1
{"name":"Tom","age":20}

那我们就可以定义一个 Person类,并加入 name和 age这两个字段,然后只需简单地调 用如下代码就可以将 JSON数据自动解析成一个 Person对象了:

1
2
Gson gson = new Gson();
Person person = gson.fromJson(jsonData, Person.class);

如果需要解析的是一段 JSON数组会稍微麻烦一点,我们需要借助 TypeToken将期望解 析成的数据类型传入到 fromJson()方法中,如下所示:

1
List<Person> people = gson.fromJson(jsonData, new TypeToken<List<Person>>() {}.getType());

-Android网络通信技术

Android手机肯定也是可以上网的,所以作为开发者的我们就需要考虑 如何利用网络来编写出更加出色的应用程序,像 QQ、微博、新闻等常见的应用都会大量地 使用到网络技术。本章主要会讲述如何在手机端使用 HTTP协议和服务器端进行网络交互, 并对服务器返回的数据进行解析,这也是 Android中最常使用到的网络技术了。

在 Android上发送 HTTP请求的方式一般有两种,HttpURLConnection和 HttpClient。

HttpURLConnection:

首先我们要获取到HttpURLConnection实例,一般只需new出一个URL对象,并传入目标的网络地址,然后调用一下 openConnection()方法即可,如下所示:

1
2
URL url = new URL("http://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

得到了 HttpURLConnection的实例之后,我们可以设置一下 HTTP请求所使用的方法。 常用的方法主要有两个,GET和 POST。GET表示希望从服务器那里获取数据,而 POST则 表示希望提交数据给服务器。写法如下:

1
connection.setRequestMethod("GET");

接下来就可以进行一些自由的定制了,比如设置连接超时、读取超时的毫秒数,以及服 务器希望得到的一些消息头等。这部分内容根据自己的实际情况进行编写,示例写法如下:

1
2
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);

之后再调用 getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是 对输入流进行读取,如下所示:

1
InputStream in = connection.getInputStream();

最后可以调用 disconnect()方法将这个 HTTP连接关闭掉,如下所示:

1
connection.disconnect();

那么如果是想要提交数据给服务器应该怎么办呢?其实也不复杂,只需要将 HTTP请求 的方法改成 POST,并在获取输入流之前把要提交的数据写出即可。注意每条数据都要以键 值对的形式存在,数据与数据之间用&符号隔开,比如说我们想要向服务器提交用户名和密 码,就可以这样写:

1
2
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.writeBytes("username=admin&password=123456");

HttpClient:

首先你需要知道,HttpClient是一个接口,因此无法创建它的实例,通常情况下都会创 建一个 DefaultHttpClient的实例,如下所示:

1
HttpClient httpClient = new DefaultHttpClient();

接下来如果想要发起一条 GET请求,就可以创建一个 HttpGet对象,并传入目标的网络 地址,然后调用 HttpClient的 execute()方法即可:

1
2
HttpGet httpGet = new HttpGet("http://www.baidu.com");
httpClient.execute(httpGet);

如果是发起一条 POST请求会比 GET稍微复杂一点,我们需要创建一个 HttpPost对象, 并传入目标的网络地址,如下所示:

1
HttpPost httpPost = new HttpPost("http://www.baidu.com");

然后通过一个 NameValuePair集合来存放待提交的参数,并将这个参数集合传入到一个 UrlEncodedFormEntity中,然后调用 HttpPost的 setEntity()方法将构建好的 UrlEncodedFormEntity 传入,如下所示:

1
2
3
4
5
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", "admin"));
params.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "utf-8");
httpPost.setEntity(entity);

接下来的操作就和 HttpGet一样了,调用 HttpClient的 execute()方法,并将 HttpPost对 象传入即可:

1
httpClient.execute(httpPost);

执行 execute()方法之后会返回一个 HttpResponse对象,服务器所返回的所有信息就会包 含在这里面。通常情况下我们都会先取出服务器返回的状态码,如果等于 200就说明请求和响应都成功了,如下所示:

1
2
3
if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 请求和响应都成功了
}

接下来在这个 if判断的内部取出服务返回的具体内容,可以调用 getEntity()方法获取到 一个 HttpEntity实例,然后再用 EntityUtils.toString()这个静态方法将 HttpEntity转换成字符串 即可,如下所示:

1
2
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity);

注意如果服务器返回的数据是带有中文的,直接调用 EntityUtils.toString()方法进行转换 会有乱码的情况出现,这个时候只需要在转换的时候将字符集指定成 utf-8就可以了,如下 所示:

1
String response = EntityUtils.toString(entity, "utf-8");

由于要联网,所以要在AndroidManifest.xml里面加入连接网络的权限:

1
<uses-permission android:name="android.permission.INTERNET" />

-Android连接数据库

Android平台下与服务器数据库通信的方法主要有:

1、直接连接:在Android工程中引入jdbc驱动,采用驱动连接数据库;

2、间接连接:在服务器上用PHP+DBMS做服务器端,PHP将DBMS中的数据用JSON或者XML进行封装,然后将数据封装成接口,给Android平台回调。

注意:采用jdbc方法主要问题是安全性不高,而且一旦需要访问的数据过多,容易出问题。另外,Android本身有对JSON或者XML数据直接解析的API,所以间接连接的方式更为可靠。

JDBC连接

JDBC是Java Data Base Connectivity的缩写,意思为“Java数据库连接”,由一组用Java语言编写的类和接口组成,为java层直接操作关系型数据库提供了标准的API。原理很简单,主要是先服务器DBMS发送SQL(结构化查询语言)指令。实现各种数据库的操作。

在Android工程使用JDBC连接数据库的主要步骤:加载JDBC驱动程序——->建立连接———>发送SQL语句。

在项目中导入jdbc驱动,然后在代码开始出import jdbc的包。

建立链接:每种DBMS的JDBC驱动是不一样的,同一个DBMS也会有几种JDBC驱动,如Microsoft SQL Server的JDBC驱动主要有两种,Microsoft 官方提供的JDBC驱动和民间开源的JDBC驱动(JTDS),推荐JTDS,bug少,而且是完全开放源代码的。目前JTDS只能支持Microsoft SQL Server和Sybase。

由于DBMS与JDBC驱动的不同,所以每种JDBC连接数据库的字符串书写方法也是不一样的。 下面给出几种常见的JDBC与DBMS建立连接的字符串书写格式

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
//1. MySQL(http://www.mysql.com)mm.mysql-2.0.2-bin.jar
Connection con = null;
Class.forName( "org.gjt.mm.mysql.Driver" );// 加载驱动程序
con = DriverManager.getConnection( "jdbc:mysql://DbComputerNameOrIPAddr:3306/DatabaseName", UserName, Password );
//2. PostgreSQL(http://www.de.postgresql.org)pgjdbc2.jar
Connection con = null;
Class.forName("org.postgresql.Driver" );// 加载驱动程序
con = DriverManager.getConnection( "jdbc:postgresql://DbComputerNameOrIPAddr/DatabaseName", UserName, Password );
//3. Oracle(http://www.oracle.com/ip/deploy/database/oracle9i/)classes12.zip
Connection con = null;
Class.forName( "oracle.jdbc.driver.OracleDriver" );// 加载驱动程序
con = DriverManager.getConnection( "jdbc:oracle:thin:@DbComputerNameOrIPAddr:1521:DatabaseName", UserName, Password );
//4. Sybase(http://jtds.sourceforge.net)jconn2.jar
Connection con = null;
Class.forName( "com.sybase.jdbc2.jdbc.SybDriver" );// 加载驱动程序
con = DriverManager.getConnection( "jdbc:sybase:Tds:DbComputerNameOrIPAddr:2638/DatabaseName", UserName, Password );
//(Default-Username/Password: "dba"/"sql")
//5. Microsoft SQLServer(http://jtds.sourceforge.net)
Connection con = null;
Class.forName( "net.sourceforge.jtds.jdbc.Driver" );// 加载驱动程序
con = DriverManager.getConnection( "jdbc:jtds:sqlserver://DbComputerNameOrIPAddr:1433/DatabaseName", UserName, Password );
//6. Microsoft SQLServer(http://www.microsoft.com)
Connection con = null;
Class.forName( "com.microsoft.jdbc.sqlserver.SQLServerDriver" );// 加载驱动程序
con = DriverManager.getConnection( "jdbc:microsoft:sqlserver://DbComputerNameOrIPAddr:1433;databaseName=master", UserName, Password );

发送SQL语句(以SQL server为例),当成功连接数据库之后,就可以发送操作数据的语句并处理结果了。在发送SQL语句之前,首先要创建一个statement对象,statement对象主要工作是把SQL语句发送到DBMS。然后发送SQL语句。对于SELECT操作,使用的是Statement对象的executeQuery(sql )方法,对于一些创建table和修改table的操作,使用的是Statement对象的executeUpdate(sql )方法。

1
2
3
4
5
// 查询表名为“table_test”的所有内容
String sql = "SELECT * FROM table_test";
// 创建Statement
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);

接口回调

调用自己服务器接口返回的数据,需要提供服务器地址,然后通过网络通信技术解析服务器返回的数据。

-Android图片处理、图片缓存机制

说到图片处理,我们得重点学习一下两个类:Bitmap、BitmapFactory。

Bitmap

Bitmap是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

重要函数

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
public void recycle() // 回收位图占用的内存空间,把位图标记为Dead
public final boolean isRecycled() //判断位图内存是否已释放
public final int getWidth()//获取位图的宽度
public final int getHeight()//获取位图的高度
public final boolean isMutable()//图片是否可修改
public int getScaledWidth(Canvas canvas)// 获取指定密度转换后的图像的宽度
public int getScaledHeight(Canvas canvas)//获取指定密度转换后的图像的高度
public boolean compress(CompressFormat format, int quality, OutputStream stream)//按指定的图片格式以及画质,将图片转换为输出流。
format:Bitmap.CompressFormat.PNG或Bitmap.CompressFormat.JPEG
quality:画质,0-100.0表示最低画质压缩,100以最高画质压缩。对于PNG等无损格式的图片,会忽略此项设置。
public static Bitmap createBitmap(Bitmap src) // 以src为原图生成不可变得新图像
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)//以src为原图,创建新的图像,指定新图像的高宽以及是否可变。
public static Bitmap createBitmap(int width, int height, Config config)——创建指定格式、大小的位图
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height)以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。

BitmapFactory

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Option 参数类:
public boolean inJustDecodeBounds//如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息。
public int inSampleSize //图片缩放的倍数
public int outWidth//获取图片的宽度值
public int outHeight//获取图片的高度值
public int inDensity//用于位图的像素压缩比
public int inTargetDensity //用于目标位图的像素压缩比(要生成的位图)
public byte[] inTempStorage //创建临时文件,将图片存储
public boolean inScaled//设置为true时进行图片压缩,从inDensity到inTargetDensity
public boolean inDither //如果为true,解码器尝试抖动解码
public Bitmap.Config inPreferredConfig //设置解码器
public String outMimeType //设置解码图像
public boolean inPurgeable//当存储Pixel的内存空间在系统内存不足时是否可以被回收
public boolean inInputShareable //inPurgeable为true情况下才生效,是否可以共享一个InputStream
public boolean inPreferQualityOverSpeed //为true则优先保证Bitmap质量其次是解码速度``
public boolean inMutable //配置Bitmap是否可以更改,比如:在Bitmap上隔几个像素加一条线段
public int inScreenDensity //当前屏幕的像素密度
工厂方法:
public static Bitmap decodeFile(String pathName, Options opts) //从文件读取图片
public static Bitmap decodeFile(String pathName)
public static Bitmap decodeStream(InputStream is) //从输入流读取图片
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeResource(Resources res, int id) //从资源文件读取图片
public static Bitmap decodeResource(Resources res, int id, Options opts)
public static Bitmap decodeByteArray(byte[] data, int offset, int length) //从数组读取图片
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd)//从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
Bitmap.Config inPreferredConfig :
枚举变量 (位图位数越高代表其可以存储的颜色信息越多,图像越逼真,占用内存越大)
public static final Bitmap.Config ALPHA_8 //代表8位Alpha位图 每个像素占用1byte内存
public static final Bitmap.Config ARGB_4444 //代表16位ARGB位图 每个像素占用2byte内存
public static final Bitmap.Config ARGB_8888 //代表32位ARGB位图 每个像素占用4byte内存
public static final Bitmap.Config RGB_565 //代表8位RGB位图 每个像素占用2byte内存

​ Android中一张图片(BitMap)占用的内存主要和以下几个因数有关:图片长度,图片宽度,单位像素占用的字节数。一张图片(BitMap)占用的内存 = 图片长度 图片宽度 单位像素占用的字节数。

下面列举几个常用方法的详细介绍:

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
29
30
31
32
33
34
/**
* 获取缩放后的本地图片
*
* @param filePath 文件路径
* @param width 宽
* @param height 高
* @return
*/
public static Bitmap readBitmapFromFileDescriptor(String filePath, int width, int height) {
try {
FileInputStream fis = new FileInputStream(filePath);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;
if (srcHeight > height || srcWidth > width) {
if (srcWidth > srcHeight) {
inSampleSize = Math.round(srcHeight / height);
} else {
inSampleSize = Math.round(srcWidth / width);
}
}
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize;
return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
} catch (Exception ex) {
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 将图片保存至文件
*
* @param filePath 文件路径
* @param b bitmap图片对象
* @param quality 图片画质
*/
public static void writeBitmapToFile(String filePath, Bitmap b, int quality) {
try {
File desFile = new File(filePath);
FileOutputStream fos = new FileOutputStream(desFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
b.compress(Bitmap.CompressFormat.JPEG, quality, bos);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
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
29
/**
* 图片压缩
*
* @param image 压缩的图片对象
* @return 返回bitmap对象
*/
private static Bitmap compressImage(Bitmap image) {
if (image == null) {
return null;
}
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);
Bitmap bitmap = BitmapFactory.decodeStream(isBm);
return bitmap;
} catch (OutOfMemoryError e) {
} finally {
try {
if (baos != null) {
baos.close();
}
} catch (IOException e) {
}
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 图片旋转角度
*
* @param b 旋转的图片对象
* @param rotateDegree 旋转角度
* @return 返回bitmap对象
*/
private static Bitmap rotateBitmap(Bitmap b, float rotateDegree) {
if (b == null) {
return null;
}
Matrix matrix = new Matrix();
matrix.postRotate(rotateDegree);
Bitmap rotaBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true);
return rotaBitmap;
}

三级图片缓存机制

在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价—流量。对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理,每次打开应用都去网络获取图片,用户可就不乐意了,所以我们得用三级缓存策略(缓存层分为三层:内存层,磁盘层,网络层),如果内存或者本地磁盘中已有所需图片,就不用通过网络层获取图片,减少流量的损耗。

关于缓存层的工作,当我们第一次打开应用获取图片时,先到网络去下载图片,然后依次存入内存缓存,磁盘缓存,当我们再一次需要用到刚才下载的这张图片时,就不需要再重复的到网络上去下载,直接可以从内存缓存和磁盘缓存中找,由于内存缓存速度较快,我们优先到内存缓存中寻找该图片,如果找到则运用,如果没有找到(内存缓存大小有限),那么我们再到磁盘缓存中去找。只要我们合理的去协调这三层缓存运用,便可以提升应用性能和用户体验。

1、内存层:(手机内存)

内存缓存相对于磁盘缓存而言,速度要来的快很多,但缺点容量较小且会被系统回收,这里的实现我用到了LruCache。

LruCache这个类是Android3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。

2、磁盘层:(SD卡)

相比内存缓存而言速度要来得慢很多,但容量很大,这里的实现我用到了DiskLruCache类。

DiskLruCache是非Google官方编写,但获得官方认证的硬盘缓存类,该类没有限定在Android内,所以理论上java应用也可以使用DiskLreCache来缓存。

这是DiskLruCache类的下载地址:http://pan.baidu.com/s/1o6tPjz8

3、网络层:(移动网络,无线网络)

这个就没什么解释的了,就是我们上网用的流量。网络访问实现可以用开源框架Volley。

开源框架Volley是2013年Google I/O大会发布的,Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。

这是Volley的下载地址:http://pan.baidu.com/s/1kThedX9

计算机基础

-数组和链表的区别

数组结构在通过索引进行查询数据时效率比较高,而对于数组插入和删除操作,则效率会比较低,在第一个位置进行插入数据,其余数据就需要依次向后移动,而第一个数据进行删除,则需要所有数据全部向前移。

链表:为了保证数据插入和删除,不会影响其他数据的移动,保证线性开销,就引出了指针链接,链表是由一系列节点组成的,每个节点都会有一个链点,next链,next链会执行下一个node的引用,所以我们在插入或者删除的时候,需要链表next链的指向地址即可,每个节点不需要内存进行连续存储,这样会减小删除和插入的线性开销。链表结构主要分为两种链表,单向链表和双向链表 ,即单向链表只有一个next链,而双向链表会有next链和pre链。

简单的区别如下:

①数组静态分配内存,链表动态分配内存;②数组在内存中连续,链表不连续;③数组元素在栈区,链表元素在堆区;④数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);⑤数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。

数组与链表的优缺点:

数组:

​ 优点:使用方便 ,查询效率比链表高,内存为一连续的区域 。 缺点:大小固定,不适合动态存储,不方便动态添加。

链表:

​ 优点:可动态添加删除,大小可变。 缺点:只能通过顺次指针访问,查询效率低。

-线程和进程

进程概念

几乎所有的操作系统都支持进程的概念,所有运行中的任务通常对应一个进程(process)。当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位。

一般而言,进程包含如下三个特征:

1.独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。

2.动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中是不具备的。

3.并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

这里需要注意:并发性和并行性是两个概念,并行指在同一时刻,有多条指令在多个处理器上同时执行;并发指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果。

进程的五种基本状态及转换

线程概念

如何能使多个程序更好地并发执行,同时又尽量减少系统的开销,已成为操作系统所追求的重要目标。主要的改进方向有两点:①并不把其作为调度和分派的基本单位也要同时作为拥有资源的单位,以做到”轻装上阵”;②对于拥有资源的基本单位,应不对之施以频繁的切换。于是,线程应运而生。

线程是进程的组成成分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程的全部资源,因此编程更加方便。但也必须更加小心,因为需要确保线程不会妨碍同一进程里的其他线程。

线程的特征:①调度的基本单位;②并发性;③拥有资源;④独立性;⑤支持多处理机系统。

多线程编程优势:

①进程之间不能共享内存,但线程之间共享内存非常容易;②系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高;③Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。

创建Java多线程

1、创建Thread的子类

创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。例子如下:

1
2
3
4
5
6
7
8
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
}
}
MyThread myThread = new MyThread();
myTread.start();

也可以如下创建一个Thread的匿名子类:

1
2
3
4
5
6
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
}
};
thread.start();

2、实现Runnable接口

第二种编写线程执行代码的方式是新建一个实现了java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。下面给出例子:

1
2
3
4
5
6
7
8
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
}
}
Thread thread = new Thread(new MyRunnable());
thread.start();

同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:

1
2
3
4
5
6
7
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
}
}
Thread thread = new Thread(myRunnable);
thread.start();

线程安全

在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。

Java同步块

Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作。所有其他等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。

有四种不同的同步块:

  1. 实例方法
  2. 静态方法
  3. 实例方法中的同步块
  4. 静态方法中的同步块

实例方法同步:Java实例方法同步是同步在拥有该方法的对象上。这样,每个实例其方法同步都同步在不同的对象上,即该方法所属的实例。

1
2
3
public synchronized void add(int value){
this.count += value;
}

静态方法同步:静态方法的同步是指同步在该方法所在的类对象上。因为在Java虚拟机中一个类只能对应一个类对象,所以同时只允许一个线程执行同一个类中的静态同步方法。

1
2
3
public static synchronized void add(int value){
count += value;
}

实例方法中的同步块:注意Java同步块构造器用括号将对象括起来。下面例子中使用了“this”,即为调用add方法的实例本身。在同步构造器中用括号括起来的对象叫做监视器对象。一次只有一个线程能够在同步于同一个监视器对象的Java方法内执行。

下面两个例子都同步他们所调用的实例对象上,因此他们在同步的执行效果上是等效的。

1
2
3
4
5
6
7
8
9
10
11
12
public class MyClass {
public synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public void log2(String msg1, String msg2){
synchronized(this){
log.writeln(msg1);
log.writeln(msg2);
}
}
}

静态方法中的同步块:下面两个方法不允许同时被线程访问。如果第二个同步块不是同步在MyClass.class这个对象上,那么这两个方法可以同时被线程访问。

1
2
3
4
5
6
7
8
9
10
11
12
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}

Java线程通信

线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。

Java有一个内建的等待机制来允许线程在等待信号的时候变为非运行状态。java.lang.Object 类定义了三个方法,wait()、notify()和notifyAll()来实现这个等待机制。

一个线程一旦调用了任意对象的wait()方法,就会变为非运行状态,直到另一个线程调用了同一个对象的notify()方法。为了调用wait()或者notify(),线程必须先获得那个对象的锁。也就是说,线程必须在同步块里调用wait()或者notify()。

以下为一个使用了wait()和notify()实现的线程间通信的共享对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyWaitNotify{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}

注意以下几点:

1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。

2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。

3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。

4、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。

notes7

2017/03/12 00:01

一、方法重载和重写的区别:

方法重载的返回值的类型可以不同,因为判断方法重载的方法主要是根据方法的参数不同来判定;方法重写的返回值类型需要相同,重写就是子类继承了父类的方法,并在此方法上重写属于自己的特征,既然是继承过来的,那么它的返回值类型就必须要相同。

二、abstract class和interface有什么区别:

1.抽象类可以有构造方法,接口中不能有构造方法。

2.抽象类中可以有普通成员变量,接口中没有普通成员变量

3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

4.抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然

eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

5.抽象类中可以包含静态方法,接口中不能包含静态方法

6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

7.一个类可以实现多个接口,但只能继承一个抽象类。

8.接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用。

三、集合对象中的线程安全:

vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的

statck:堆栈类,先进后出

hashtable:就比hashmap多了个线程安全

enumeration:枚举,相当于迭代器

除了这些之外,其他的都是非线程安全的类和接口。

四、JAVA反射机制主要提供了哪些功能?

Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

五、

  1. finally{}代码块比return先执行

2.多个return是按顺序执行的的,多个return执行了一个后,后面的return就不会执行了。

3.记住一点,不管有不有异常抛出, finally都会在return返回前执行。

notes6

1、Java初始化过程是这样的:

①首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;

②然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;

③其次,初始化父类的普通成员变量和代码块,在执行父类的构造方法;

④最后,初始化子类的普通成员变量和代码块,在执行子类的构造方法。

2、Java hashmap:

①HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”;

②HashMap 的实现不是同步的,意味着它不是线程安全的;

③HashMap中的key-value都是存储在Entry数组中的;

④HashMap通过链地址法解决哈希冲突。

3、java中可以有多个重载的main方法,但只有public static void main(String[] args){}是函数入口。

4、JSP内置对象:

request response session out page application exception pagecontext config。

5、视图更新的限制:

①初始View定义的Select语句里如果包含了GROUP BY,DISTINCT,LIMIT或HAVING等命令时;

②如果视图里数据来自多张字表时。

6、结构型模式:

结构型模式是描述如何将类对象结合在一起,形成一个更大的结构,结构模式描述两种不同的东西:类与类的实例。故可以分为类结构模式和对象结构模式。

在GoF设计模式中,结构型模式有:

①.适配器模式 Adapter

适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。两个成熟的类需要通信,但是接口不同,由于开闭原则,我们不能去修改这两个类的接口,所以就需要一个适配器来完成衔接过程。

②.桥接模式 Bridge

桥接模式将抽象部分与它的实现部分分离,是它们都可以独立地变化。它很好的支持了开闭原则和组合锯和复用原则。实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这些多角度分离出来让他们独立变化,减少他们之间的耦合。

③.组合模式 Composite

组合模式将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

④.装饰模式 Decorator

装饰模式动态地给一个对象添加一些额外的职责,就增加功能来说,它比生成子类更灵活。也可以这样说,装饰模式把复杂类中的核心职责和装饰功能区分开了,这样既简化了复杂类,有去除了相关类中重复的装饰逻辑。装饰模式没有通过继承原有类来扩展功能,但却达到了一样的目的,而且比继承更加灵活,所以可以说装饰模式是继承关系的一种替代方案。

⑤.外观模式 Facade

外观模式为子系统中的一组接口提供了同意的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式中,客户对各个具体的子系统是不了解的,所以对这些子系统进行了封装,对外只提供了用户所明白的单一而简单的接口,用户直接使用这个接口就可以完成操作,而不用去理睬具体的过程,而且子系统的变化不会影响到用户,这样就做到了信息隐蔽。

⑥.享元模式 Flyweight

享元模式为运用共享技术有效的支持大量细粒度的对象。因为它可以通过共享大幅度地减少单个实例的数目,避免了大量非常相似类的开销。享元模式是一个类别的多个对象共享这个类别的一个对象,而不是各自再实例化各自的对象。这样就达到了节省内存的目的。

⑦.代理模式 Proxy

为其他对象提供一种代理,并由代理对象控制对原对象的引用,以间接控制对原对象的访问。

7、继承具有多态性,可以是本类的引用指向本类对象,也可以是本类的引用指向子类对象。(向上转型是无条件的。)

8、1.静态代码块 2.构造代码块3.构造方法的执行顺序是1>2>3。

9、构造方法每次都是构造出新的对象,不存在多个线程同时读写同一对象中的属性的问题,所以不需要同步 。一个子类可以覆盖掉父类的同步方法。

notes5

1、静态链接库与动态链接库的优点:

静态链接库:代码装载速度快,执行速度略比动态链接库快;

动态链接库:共享、开发模式好、减少界面交换。

2、由权值分别为1、12、13、4、8的叶子节点生成一颗哈夫曼树,它的带权路径长度为 :

img

3、在读取磁盘上多个顺序数据块时的效率最高:通道方式。

I/O中断方式是以字节为单位,DMA控制方式是以一个连续的数据块为单位,I/O通道控制方式是DMA控制方式的发展,是以一组数据块为单位的,即可以连续读取多个数据块。

4、进程间的通信方式:管道、共享内存、消息队列、socket、信号量。

5、TCP/IP建立连接的三次握手:第一次握手:客户端向服务器端发送SYN包(syn=j),进入SYN_SEND状态,等待服务器确认。第二次握手:服务器收到SYN包,确认SYN,此时syn=j+1,同时发送一个SYN包(syn=k)即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到SYN+ACK包,向服务器发送ACK确认包,此时客户端和服务器端均进入ESTABLISHED状态。

其中有一个半连接状态:服务器维护一个半连接队列,该队列卫每个客户端SYN包开设一个条目,标明服务器已经接到SYN包,并向客户端发出确认,这些条目表示的连接处于SYN_RECV状态,得到客户端的确认后进入ESTABLISHED状态。

6、同一进程中的线程不共享的部分是:栈空间,栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈相互独立,是线程安全的。

7、虚拟局域网(VLAN)是一组逻辑上的设备和用户,这些设备和用户并不受物理位置的限制,可以根据功能、部门及应用等因素将它们组织起来,相互之间的通信就好像它们在同一个网段中一样,由此得名虚拟局域网。VLAN是一种比较新的技术,工作在OSI参考模型的第2层和第3层,一个VLAN就是一个广播域,VLAN之间的通信是通过第3层的路由器来完成的。与传统的局域网技术相比较,VLAN技术更加灵活,它具有以下优点: 网络设备的移动、添加和修改的管理开销减少;可以控制广播活动;可提高网络的安全性。

在计算机网络中,一个二层网络可以被划分为多个不同的广播域,一个广播域对应了一个特定的用户组,默认情况下这些不同的广播域是相互隔离的。不同的广播域之间想要通信,需要通过一个或多个路由器。这样的一个广播域就称为VLAN。

salary management system

1 范围与前景

1.1. 课程设计选题以及目的

选题:《工资管理系统》

目的:通过数据库系统课程设计,熟悉了SQL SERVER数据库管理系统的结构与组成;掌握了SQL SERVER数据库管理系统的应用技术和的使用;应用JSP+SQL Server开发工具实践了《工资管理系统》的数据库应用系统的设计方法、开发过程和SQL SERVER数据库的管理与维护。最终达到掌握数据库管理系统的使用和开发,提高分析问题、解决问题和实践应用能力。

1.2. 应用背景

众所周知当今时代的世界经济正从工业经济向知识经济转变,而知识经济的两个重要特征就是信息化和全球化。要实现信息化和全球化就必须依靠完善的网络和数据库机制。处于现在这样一个飞速发展的信息时代,在各行各业中都离不开信息处理,这正是计算机被广泛应用于管理信息系统的环境,使用计算机进行信息控制提高了工作效率。

在计算机诞生前,对工资的管理一直都是纸质化记录、人工统计和计算。这样的管理不但费时费力,也很容易计算上的错误与疏漏。这种管理方式效率低、保密性差、不利于添加、删除、查询、更改员工的工资信息,久而久之还会产生大量的文件,给维护带来了不少困难。当今社会,资金是企业生存的主要元素,资金的流通影响到整个企业的运作,企业员工工资是企业资金管理的一个重要组成部分,因为每个月企业都要涉及到发放员工工资的问题。而传统的纸质材料的数据信息管理方式对于工资的管理与发放又存在很大麻烦,所以如果能够实现工资管理自动化,无疑给企业管理部门带来很大方便。随着计算机的诞生,实现工资管理的系统化、规范化、自动化,将成为现代企业管理工资的首选。

1.3. 业务机遇

企业的工资管理部门通过工资管理系统对员工工资信息进行录入、更新、删除、更改、添加、统计、排序、查询、打印,可以提高工作效率,减少工资信息的疏漏。减少人力的投入,节约开支,快速准确的管理员工的工资信息,准确按时的发放工资。通过详细的奖惩机制,系统准确计算每个员工工资的奖惩情况,起到监督作用,同时提高员工的工作热情。员工可以通过在工资管理系统中查询或者打印自己的工资详细记录,对自己的工资情况一目了然,减少有关工资发放方面的纷争,提高员工对企业的信赖度。

工资管理系统,是企业正规化管理的必然需求,不仅能提高企业管理质量,还能方便员工查询自己的工资信息,于老板于员工,都有好处。

1.4. 业务风险

(1)存贮数据的数据库被破坏导致数据的缺失或丢失;

​ 可能性0.3,影响9

(2)企业电脑被入侵导致员工工资信息的泄露;

​ 可能性0.1,影响9

(3)自身系统技术设计存在漏洞,导致工资操作出现bug;

​ 可能性0.2,影响8

(4)企业不满意系统的开发情况

​ 可能性0.3,影响7

1.5. 前景概述

工资管理系统是一个基于互联网的应用程序,有两个登录模式:普通用户(员工)、管理员用户。在普通用户模式中,员工可以用自己的工号以及登录密码访问系统,然后查询自己的个人资料、工资信息,没有操作其他普通用户的权限;在管理员模式中,管理员用工号以及登录密码访问系统,管理员权限相对较多,可以增加员工个人档案,删除、修改已存在的员工信息,还可以统计员工的工资信息,当管理员离职时,系统应该回收其登录权限。

产品名称:工资管理系统

产品面向对象:企业工资管理员、企业员工

产品优势:区别于传统的纸质版信息管理,运用此系统管理工资,高效准确,操作便捷。

1.6. 主要特征

FE-1:普通用户(员工)登录模式下,员工只能操作查询自己的信息,包括个人档案以及工资信息;

FE-2:普通用户(员工)登录模式下,员工可以在查询工资信息功能模块下,打印自己的工资查询结果;

FE-3:普通用户(员工)登录模式下,员工可以修改自己的登录密码;

FE-4:普通用户(员工)需要提交个人档案进行实名注册;

FE-5:管理员登录模式下,管理员可以添加审核通过的员工个人档案,添加普通用户;

FE-6:管理员登录模式下,可以添加、修改、删除用户;

FE-7:管理员登录模式下,可以添加、修改、删除员工个人档案;

FE-8:管理员登录模式下,可以添加、修改员工基本工资;

FE-9:管理员登录模式下,可以管理、查询发放工资;

FE-10:普通用户(员工)/ 管理员登录系统之前,应该选择相应的登录模式,才可准确登录。普通用户(员工)/ 管理员在操作结束后,可以退出系统。

1.7. 假设与依赖

AS-1:普通用户(员工)在领取工资后打印证条,工资管理系统存底,过后如有工资纠纷,可查询有关记录作为凭据;

AS-2:企业工资按时发放;

AS-3:管理员需要每半年对员工个人档案进行更新存档,以确保信息有效。

1.8. 限制与排除

LI-1:管理员能查询员工个人隐私信息,应当让管理员签订保密协议,确保员工个人隐私不被泄露;

LI-2:员工初始使用本系统可能不适应,不过系统在设计以及实现过程中,尽量简化使得操作方便;

LI-3:员工或者管理员离职后,个人信息应该存档半年,以备不时之需。

1.9. 项目运行环境

本系统采用JSP+SQL Server开发,使用的IDE是Myeclipse,使用的服务器是Tomcat。

①操作系统:Windows

②数据库:SQL server

③运行服务器:Tomcat

④系统操作平台:Myeclipse集成操作环境 / IE、360、等常用浏览器

⑤PC电脑一台。

2 需求分析

2.1. 功能需求

本系统设计内容是一个基于web的工资管理系统,通过分析调查,再结合案例,我认为系统开发出来的功能应该包括:

(1)系统用户分为两种:普通用户(员工)和管理员。管理员顾名思义,能管理操作普通用户;普通用户能修改登录密码、查询个人档案和工资信息、打印工资内容。

(2)能实现管理员对员工个人档案的录入、修改、删除。

(3)管理员能设定员工的基本工资。

(4)按照不同工种的基本工资情况、员工的考勤情况、员工的奖惩情况产生月工资单表。

(5)系统应提供强大的查询、计算功能。

(6)可以导出工资报表。

通过对系统功能需求的初步分析,得出系统的功能模块图如下:

​ 系统功能模块图

2.2. 系统数据流图

​ 图1 0层数据流图

​ 图2 工种基本工资设定数据流图

​ 图3 工资核算数据流图

​ 图4 员工信息查询数据流图

​ 图5 管理员更新工资管理系统数据流图

2.3. 概念模型分析

2.3.1. 实体属性图

①用户实体包括用户名、用户密码、用户权限等:

②员工实体包括员工编号、姓名、性别、生日、部门、职务等:

③员工基本工资实体包括员工编号、基本工资、岗位工资、工龄工资、生活补贴、住房补贴、交通补贴等:

④员工工资信息包括员工编号、工资月份、加班工资、缺勤扣款、本月奖金等:

2.3.2. 实体属性联系图(E-R图)

3 数据库设计

3.1. 数据库逻辑结构

本次课程设计中数据库设计采用的逻辑模型是关系模型,关系模型包括关系数据库结构、关系数据操作集、关系的完整性约束。

逻辑结构设计的任务就是把概念模型设计阶段设计好的基本E-R图转换为与选用的具体机器上的DBMS产品所支持的数据模型相符合的逻辑结构,现选用关系模型,E-R图向关系模式的转换结果如下所示:

用户表(用户名、用户密码、用户权限),主码是用户ID;

员工个人档案表(员工ID、员工名称、性别、所属部门、出生日期、籍贯、婚姻情况、身份证号、政治身份、所属名族、教育水平、专业、毕业日期、毕业大学、公积金账号、行政级别、职位、职称、外语、外语水平、计算机水平、联系电话、手机号码、住址、在职情况、用工形式、个人简历),主码是员工ID;

员工基本工资表(员工ID、基本工资、岗位工资、工龄工资、生活补贴、住房补贴、交通补贴、公积金、经手人),主码是员工ID,员工ID也是外码,参照员工个人档案表的员工ID;

员工工资信息表(员工ID、工资月份、加班工资、本月奖金、病假扣除、事假扣除、缺勤扣除、其他扣除、个人所得税),主码是组合主码(员工ID、工资月份),员工ID是外码,参照员工个人档案表的员工ID。

3.2. 物理模型设计

3.3. 数据库完整性设计

主要是对数据库表的三种完整性进行设计:

实体完整性:通过设置主键都已实现;

参照完整性:员工基本工资表和员工工资信息表中的员工编号均是外码,参照的是员工个人档案信息中的员工编号,这么设计的原因是:在系统中存在工资信息的记录,就应该有相应的档案存在才对,也就是说,发工资的对象一定是公司存在的员工对象。

用户自定义完整性:在该系统中,设置了员工个人档案表中性别一栏的值必须是‘男’或者‘女’。

3.4. 数据库安全性设计

3.4.1. 用户权限设置

该系统的用户只有两种,一种是管理员,另一种是普通员工用户,管理员具有对数据库操作的所有权限,普通员工用户只有对自己的工资信息以及个人档案查询的权限,对数据库中其他对象的操作权限将受限。

3.4.2. 数据库备份

数据库系统可能发生各种各样的故障,大致可分为以下几类:事务内部故障、系统故障、介质故障、计算机故障,总结各类故障,对数据库的影响有两种可能性:一是数据库本身被破坏,二是数据库没有被破坏,但数据可能不正确,这是由于事务的运行被非正常终止造成的。然而,数据库遭到破坏后,我们就得想办法看能不能恢复数据库,恢复机制涉及两个关键问题是:第一,如何建立冗余数据;第二,如何利用这些冗余数据实施数据库恢复。

存储在计算机当中的数据库可能会因为某一些不确定的因素而导致破坏,这样子的话,系统信息就会遗失,对于正规大型的信息管理系统来说,这无疑是巨大的隐患。考虑到系统信息能安全保存,DBMS(SQL Server)中建立的存在于本地的数据库信息,包括mdf(数据转储)文件和ldf(登录日志)文件,我将其定期拷贝到其他备份计算机上和U盘上,以确保在本机数据库存在错误的情况下,能由建立的冗余数据恢复数据库,维护系统信息的安全与完整。

3.5. 触发器设计

(1)为实现添加员工信息后自动添加用户,删除员工信息后自动删除用户,我定义了如下触发器:

create trigger AddUser on [dbo].[employee] for insert

as begin

declare @EmployeeID char (20)

select @EmployeeID = inserted.EmployeeID

​ from inserted

​ insert into users values(@EmployeeID,@EmployeeID,’普通用户’)

End

create trigger delete1 on [dbo].[employee]

for delete

as

begin

​ delete dbo.users

​ from users,deleted

​ where users.ID=deleted.EmployeeID

end

测试:

users表一开始我只是在其中插入了一条元组,新建了一个管理员用户binbin:

接着,我在employee表中插入一条元组,新建了一个员工档案,员工编号为000001:

再回来刷新users表的时候,就自动加上了一个新的普通登录用户,id为000001:

可见,触发器设置成功了。同理,删除触发器也是这个效果。

(2)实现个人所得税的自动计算,而不在程序中修改,我使用了一个计算个人所得税的触发器,代码如下:

Create trigger tax on [dbo].[pay]

for insert

As

// 工资低于1600的不收税,高于1600的按层次收税,工资越高,税收越高。

update pay

set tax=0

where EmployeeID=pay.EmployeeID and PaySum-SickLeave-PersonalLeave-Absence-Others between 0 and 1600

update pay

set tax=(PaySum-SickLeave-PersonalLeave-Absence-Others-1600)*0.05

where EmployeeID=pay.EmployeeID and (PaySum-SickLeave-PersonalLeave-Absence-Others-1600) between 0 and 500

update pay

set tax=(PaySum-SickLeave-PersonalLeave-Absence-Others-1600)*0.1-25

where EmployeeID=pay.EmployeeID and (PaySum-SickLeave-PersonalLeave-Absence-Others-1600) between 500 and 2000

update pay

set tax=(PaySum-SickLeave-PersonalLeave-Absence-Others-1600)*0.15-125

where EmployeeID=pay.EmployeeID and (PaySum-SickLeave-PersonalLeave-Absence-Others-1600) between 2000 and 5000

update pay

set tax=(PaySum-SickLeave-PersonalLeave-Absence-Others-1600)*0.20-375

where EmployeeID=pay.EmployeeID and (PaySum-SickLeave-PersonalLeave-Absence-Others-1600) between 5000 and 20000

update pay

set tax=(PaySum-SickLeave-PersonalLeave-Absence-Others-1600)*0.25-1375

where EmployeeID=pay.EmployeeID and (PaySum-SickLeave-PersonalLeave-Absence-Others-1600) between 20000 and 40000

4 详细设计与实现

4.1. 数据库连接

数据库连接使用的方式是连接池,连接池主要设计思路是:

1.连接池配置属性DBbean:里面存放可以配置的一些属性

2.连接池接口IConnectionPool:里面定义一些基本的获取连接的一些方法

3.接口实现ConnectionPool:对上面操作进行实现,并加入一些其他方法

4.连接池管理ConnectionPoolManager:管理所有的不同的连接池,所有的连接都能通过这里进行从而获得连接。

使用连接池的好处:

1. 资源重用

2. 更快的系统响应速度

3. 统一的连接管理,避免数据库连接泄漏。

4.2. 系统登录功能

本页面是用户登录系统的页面,当用户提交登录信息,对用户进行身份及权限判断,再转入不同的页面。当用户名或密码、权限输入错误,会提示用户重新输入。为提高系统的安全性,没有登录是不能打开其它页面的,同样,权限为普通用户的用户也不能打开管理员的页面,而管理员则具有所有权限。

4.3. 系统管理功能

该功能可以实现修改密码、帮助功能。在修改密码时,有相应限制,如旧密码应与数据库的密码相同,新密码应输入两次,以确保不会输错。

4.4. 用户管理功能

该功能可以添加系统用户,当用户已存在时会提示不能添加:

该功能可以修改系统用户信息,如修改用户名、用户密码、用户权限,删除用户。

4.5. 员工信息管理功能

该功能可以添加员工信息功能,文本框后面加了*号的则为必填项目,如不填则不能添加该员工信息,因前面在设计数据库时添加了触发器,所以在此添加员工信息,则会自动添加以该员工号为用户名和密码的系统用户。

如需管理员工信息,可以点击修改,点击后会弹出该员工的信息,可以在相应文本框内修改。

员工查询,在这里,可实现丰富的查询功能,查询条件可叠加,选择查询条件后点击查询,会弹出查询结果,在该页面点击员工号可查看该员工的详细信息。

4.6. 基本工资管理

基 本工资管理功能可添加未有基本工资信息员工的基本工资,通过点击操作,将待添加基本工资的员工移动到右边,同时在下面的文本框内输入工资信息。

该功能同时也可修改基本工资信息,只需点击修改,则会弹出该行员工的基本工资信息,可以相应的文本框内进行修改。

4.7. 工资发放

该功能与基本工资类似,在该页面可以选择已添加基本工资的员工,没有基本工资信息的员工是不能发放工资,同样通过点击操作,可把想要发放工资的员工移动到右边,然后在下面的文本框内输入工资信息,若该员工该月的工资信息已存在,则会提示不能添加。

管理功能则与基本工资管理类似,这里不再介绍:

4.8. 工资查询功能

工资查询功能可实现丰富的查询功能,跟员工信息查询功能一样,查询条件也可叠加,选择查询条件后点击查询弹出查询结果。

在该页面可对查询结果进行打印。

点击查询结果的员工编号,会弹出该员工的详细工资信息,同样可以要求进行打印操作:

4.9. 普通用户登录

普通用户登录后能执行的功能比较少,只能修改密码、查看个人信息、查看工资信息。

操作与管理员的操作类似,这里不再详细介绍了。

后言

关于本工资管理系统就介绍这么多了,有什么不对的地方还望多多指教,也欢迎大家关注我(简书/GitHub

谢谢观看此文。

源代码地址:https://pan.baidu.com/s/1pL4QMl9

提取密码:bve2

notes4

1、折半查找、顺序查找、分块查找,平均查找长度的比较:

折半查找最小。分快查找,是将顺序表分为若干块,块内元素顺序任意,块间有序,即前一块中的最大值小于后一块中的最小值。并且有一张索引表,每一项存放每一块的最大值和指向该块第一个元素的指针。索引表有序,块内无序。所以,块间查找用二分查找,块内用顺序查找,效率介于顺序和二分之间。

2、链式存储的有序数据不能使用折半查找,因为无法直接访问折半时一半的位置处的元素。

3、一趟快速排序意思是:寻找一个支点,将该序列位置整个调整一边,

注意:支点不一定是左边第一个数,可以任意选的。

4、KMP算法下,长为n的字符串中匹配长度为m的子串的复杂度为

最好情况:每趟匹配不成功都是在第一个字符,即每趟都只需匹配一次就知道该趟是否匹配。O(m+n)

最坏情况:每趟匹配不成功都是在最后一个字符。时间复杂度O(m*n)。

5、hadoop 有单机版,伪分布一个节点,分布式。

notes3

1、

Android:

使用SimpleAdapter作为适配器时,支持三种类型的 View,而且是按照如下顺序进行匹配:

①.继承Checkable接口

②.TextView

③.ImageView。

2、串的模式匹配问题:

①STL算法:C语言:return A.find(B),Java:return A.indexOf(B);

②public String substring(int beginIndex,int endIndex)

返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,一直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。

③:String.charAt(i),定位字符串第i个位置的字符

3、贪心算法的应用:奖学金问题

小v今年有n门课,每门都有考试,为了拿到奖学金,小v必须让自己的平均成绩至少为avg。每门课由平时成绩和考试成绩组成,满分为r。现在他知道每门课的平时成绩为ai ,若想让这门课的考试成绩多拿一分的话,小v要花bi 的时间复习,不复习的话当然就是0分。同时我们显然可以发现复习得再多也不会拿到超过满分的分数。为了拿到奖学金,小v至少要花多少时间复习。

解题思路:

动态规划,此题用贪心算法的思维解决:先用用时较少的课程(所以需要一个排序算法,将输入的课程按照用时bi的大小进行排序,程序中使用的是冒泡排序),如果还未达到总分,继续添加下一门课程的计算。

判断是否已达到获取奖学金的分数:

while(score < need && j < n){

​ if(arr[j][0] < r){

​ time += arr[j][1];

​ arr[j][0]++;

​ score++;

​ }else{

​ j++;

​ }

​ }

按多加1分所需时间bi排序算法如下:

public static void sort(long a[][]){

​ int length = a.length;

​ for(int i = 0;i < length-1;i++){

// boolean flag = true;

​ for(int j = 0;j < length-1-i;j++){

​ if(a[j][1] > a[j+1][1]){

​ long tempA = a[j][0];

​ long tempB = a[j][1];

​ a[j][0] = a[j+1][0];

​ a[j][1] = a[j+1][1];

​ a[j+1][0] = tempA;

​ a[j+1][1] = tempB;

// flag = false;

​ }

​ }

// if(flag) return;

​ }

​ }

算法:

4、KMP算法和Floyd算法都不是贪心算法,Floyd算法使用的是动态规划,KMP算法则是对串的前缀进行去处理得到所有可能出现匹配的位置从而减少不必要的位移。

5、

6、二分查找 index = (rear + front ) / 2;

7、有个长度为12的无重复有序表,按折半查找法进行查找,在表内各元素等概率情况下,查找成功所需的平均比较(三元比较)的次数为 ,按照一颗完全二叉树来考虑,12个结点是4层,所以为(11+22+43+54)/12。

8、快速排序的worst case就是基本逆序或者基本有序的情况。

9、基本排序算法的稳定性:

首先,稳定性是指如果待排序数据中有多个相同的元素,如果在排序完后相同元素的顺序不发生改变,则为稳定的排序,否则为非稳定的排序。

A.快速排序就是利用切分算法依次确定n个元素的最终位置。所以,我们直接来分析切分算法。举例来说, 有待切分子数组: 5 8 3 7 3 9 7,切分元素为5,从后遍历得到第一个小于切分元素5的数据为3(index = 5),从前遍历得到第一个大于5的元素为8,交换3和8,这样,元素3的稳定性就被破坏了。

B.冒泡排序为依次从前往后与相邻值比较交换从而将未排序的最大值移至最后。如 9 8 6 9 0 ,当第一个9移至 8 6 9 9 0时,只要我们保证只有当前一个元素大于(而不是大于等于)下一个元素时才交换,就可保证排序的稳定性。

C.选择排序为每一次从第i个元素后面选择一个最小元素放入第i个位置.(i从0 到 n -1)。

举例 4 5 4 3,在第一轮交换中,我们找到最小元素3(index = 3),需要将其与第一个元素 4(indx = 1)。我们发现,当第i个元素和它后面的最小元素中间还存在 元素i 时将破坏元素i的稳定性。

D.归并排序同样是分治并合并两个有序子数组的过程。因此,和快速排序一样,我们只需要考察合并有序子数组的算法即可。 当合并有序子数组时,我们会利用辅助数组,每一次都将两个子数组中较小的元素放入即可辅助数组,直至全部放入。只要我们保证当遍历到当前两个子数组值相同时,先将第一个元素放入即可保证排序后的稳定性。所以,归并排序是稳定性。

notes2

1、event、semaphore、mutex是内核对象,能跨进程使用,critical section不能跨进程,只能实现进程内互斥。

2、

大多数 JVM 将内存区域划分为 Method Area(Non-Heap)(方法区) ,Heap(堆) , Program Counter Register(程序计数器) , VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的),Native Method Stack ( 本地方法栈 ),其中Method Area 和 Heap 是线程共享的 ,VM Stack,Native Method Stack 和Program Counter Register 是非线程共享的。为什么分为 线程共享和非线程共享的呢?请继续往下看。

首先我们熟悉一下一个一般性的 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件(以 class 为扩展名),每个java程序都需要运行在自己的JVM上,然后告知 JVM 程序的运行入口,再被 JVM 通过字节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?

概括地说来,JVM初始运行的时候都会分配好 Method Area(方法区) 和Heap(堆) ,而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说知发生在Heap上)的原因。

3、jdbc statement:

a.Statement:

普通的不带参的查询SQL;支持批量更新,批量删除;

b.PreparedStatement:

可变参数的SQL,编译一次,执行多次,效率高;

安全性好,有效防止Sql注入等问题;

支持批量更新,批量删除;

c.CallableStatement:

继承自PreparedStatement,支持带参数的SQL操作;

支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持。

4、有关Spring的事务传播特性:

PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。

5、servlet service:

a、不管是post还是get方法提交过来的连接,都会在service中处理

b、service()是在javax.servlet.Servlet接口中定义的

c、service判断请求类型,决定是调用doGet还是doPost方法

d、doGet/doPost 则是在 javax.servlet.http.HttpServlet 中实现的

6、servlet的生命周期:创建servlet的实例是由servlet容器来完成的,创建servlet实例是在初始化方法init()之前。

7、JVM中一个字节以下的整型数据会在JVM启动的时候加载进内存,除非用new Integer()显式的创建对象,否则都是同一个对象。

8、

运行时异常: 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

非运行时异常 (编译异常): 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

9、有关jsp中静态include和动态include的区别

动态 INCLUDE 用 jsp:include 动作实现 它总是会检查所含文件中的变化 , 适合用于包含动态页面 , 并且可以带参数。各个文件分别先编译,然后组合成一个文件。

静态 INCLUDE 用 include 伪码实现 , 定不会检查所含文件的变化 , 适用于包含静态页面 <%@ include file=”included.htm” %> 。先将文件的代码被原封不动地加入到了主页面从而合成一个文件,然后再进行翻译,此时不允许有相同的变量。

|