Tim's blog

始于技术 不止于技术


  • Home

  • About

  • Tags

  • Categories

  • Archives

  • guestbook

  • Search

最冤枉的sizeof

Posted on 2018-04-01 | In C/C++ |

之前一直以为sizeof(char)、sizeof(int)…居然一直以为sizeof是函数,其实sizeof在C中只是一个关键字!!!
之前一直以为sizeof(char)、sizeof(int)…居然一直以为sizeof是函数,其实sizeof在C中只是一个运算符!!!
之前一直以为sizeof(char)、sizeof(int)…居然一直以为sizeof是函数,其实sizeof在C中只是一个操作符!!!

在 Pascal 语言中,sizeof() 是一种内存容量度量函数,功能是返回一个变量或者类型的大小(以字节为单位)
① 在 C 语言中,sizeof() 是一个判断数据类型或者表达式长度的运算符(以字节为单位)

在Pascal 语言与C语言中,对 sizeof() 的处理都是在编译阶段进行;

② sizeof内部的表达式不参与运算

指针记录了另一个对象的地址。既然是来存放地址的,那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的返回值必定是4(注意结果是以字节为单位),但是,在64位系统中指针变量的sizeof结果为8

注意sizeof和strlen的区别:
1、strlen(char*)函数求的是字符串的实际长度,直到遇到第一个’\0’,然后就返回计数值,且不包括’\0’。而sizeof()函数返回的是变量声明后所占的内存数,不是实际长度

2、sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小

3、sizeof是算符,strlen是函数

4、sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以’’\0’’结尾的

5、sizeof还可以用函数做参数,比如:short f(); printf(“%d\n”,sizeof(f())); 结果是sizeof(short),即2

6、数组做sizeof的参数不退化,传递给strlen就退化为指针了

7、大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因

8、strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小

9、sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数

10、数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址

C语言关键字

Posted on 2018-03-28 | In C/C++ |

首先看看这份我总结的这份C语言大纲,大概也就知道C语言的关键字处在那个地位了,同时也可以作为一份复习的资料,虽然C语言看起来就这么一些知识点,但是我只能说我看到的知识C语言的冰山一角,想要彻底了解C语言,还是需要多看看书,甚至可以尝试去写一个C的编译器,也算是一大壮举了,语言的特性只有编译器的设计者最清楚!

今天从关键字开始说起:C语言的关键字共有32个,根据关键字作用,可分其为数据类型关键字、控制语句关键字、存储类型关键字和其它关键字四类。

1、数据类型关键字(12个):

  • char :声明字符型变量或函数
  • double :声明双精度变量或函数
  • enum :声明枚举类型
  • float:声明浮点型变量或函数
  • int: 声明整型变量或函数
  • long :声明长整型变量或函数
  • short :声明短整型变量或函数
  • signed:声明有符号类型变量或函数
  • struct:声明结构体变量或函数
  • union:声明联合数据类型
  • unsigned:声明无符号类型变量或函数
  • void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)

2、控制语句关键字(12个):
循环语句

  • for:一种循环语句(可意会不可言传)
  • do :循环语句的循环体
  • while :循环语句的循环条件
  • break:跳出当前循环
  • continue:结束当前循环,开始下一轮循环
    条件语句
  • if: 条件语句
  • else :条件语句否定分支(与 if 连用)
  • goto:无条件跳转语句
    选择语句
  • switch :用于开关语句
  • case:开关语句分支
  • default:开关语句中的“其他”分支
    返回语句
  • return :子程序返回语句(可以带参数,也看不带参数)

3、存储类型关键字(4个)

  • auto :声明自动变量 一般不使用
  • extern:声明变量是在其他文件正声明(也可以看做是引用变量)
  • register:声明寄存器变量
  • static :声明静态变量 ,static修饰全局变量的时候改变全局变量作用范围,是的全局变量只能在当前文件使用

4、其它关键字(4个):

  • const :声明只读变量
  • sizeof:计算数据类型长度
  • typedef:用以给数据类型取别名
  • volatile:说明变量在程序执行中可被隐含地改变

特殊说明一下几个原来不熟悉的关键字:

  • union :声明联合体类型
  • extern:声明变量是在其他文件正声明(也可以看做是引用变量)
  • register:声明寄存器变量,但只是建议编译器对变量声明为寄存器变量,这完全取决于编译器,有时就算不声明register,编译器也会把变量声明为寄存器变量来提高运算效率
  • volatile:这个关键字的作用是防止编译器将变量优化为寄存器变量,保证变量的内存可见性。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份

使用服务注册特殊广播接收者

Posted on 2018-03-09 | In 移动开发 |

Android中的广播:系统在运行过程中,会发生很多事件,系统为了让其他应用知道系统发生了这个事件,会发送一个对应事件的广播,比如:电量改变,收到短信,拨打电话,屏幕解锁,系统开机,只有注册一个广播接收者,就可以接收到系统发送的广播。

屏幕锁屏和解锁、电量改变等广播属于安卓系统中操作特别频繁的广播事件,若在MainActivity中注册,当MainActivity销毁时无法接收广播,所以应该在服务中去注册广播接收者,必须使用代码注册!

首先这是定义的广播接收者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyReceiver extends BroadcastReceiver { 
@Override
public void onReceive(Context context, Intent intent) {
//获取当前事件类型
String action = intent.getAction();
if("android.intent.action.SCREEN_OFF".equals(action)){
//屏幕锁屏
System.out.println("屏幕锁屏");

}else if("android.intent.action.SCREEN_ON".equals(action)){
//屏幕解锁
System.out.println("屏幕解锁");
}
}
}

动态注册广播的Service:

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
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;

public class ScreenService extends Service {
private MyReceiver myReceiver;
public ScreenService() {
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
//获取MyReceiver实例
myReceiver = new MyReceiver();

//添加Action
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
//动态注册广播
registerReceiver(myReceiver,filter);
super.onCreate();
}

@Override
public void onDestroy() {
//当服务销毁的时候取消注册广播
unregisterReceiver(myReceiver);
super.onDestroy();
}
}

MainActivity在加载的时候就开启服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package useservice.xpu.nevergiveup.serviceresgitreceiver;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(getApplicationContext(),ScreenService.class));
}
}

之后别忘记配置一下Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="useservice.xpu.nevergiveup.serviceresgitreceiver">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".ScreenService"/>
</application>
</manifest>

程序员偷偷深爱的9个不良编程习惯

Posted on 2018-03-07 | In 编码规范 |

哈哈,这篇文章还是非常能说明问题的,实际开发中必须要注意的地方!
下面这9个编码习惯,虽然在编程规则中是被驳斥的,但我们很多人就是会不由自主地使用它们。
我们曾经都做过这样的事情:当妈妈不注意的时候,偷偷地吃糖果零食,然后导致有了蛀牙。同样的,我们都违背过一些编程的基本规则,并且都会坚定地表示这种行为是不可取的。但我们就是偷偷爱着这些不良的编程习惯。

我们对所谓的编程规则嗤之以鼻,输出的代码也很糟糕——但我们依然活着。编程上帝没有下闪电劈死我们,我们的电脑也没有爆炸。事实上,只要我们能编译和发布代码,客户似乎就很满意了。

这是因为糟糕的编程不像安装电路或者摸老虎屁股那样有直接的危害性。大多数时间里它也是可以工作的。规则通常是作为一种指导或格式上的建议,并没有硬性规定一定要遵守,也不会导致代码马上死掉。当然,你的代码可能会被人耻笑,甚至可能大家公开嘲笑你,不过,这种挑战惯例的行为可以让人增加一点颠覆传统的快感,哪怕是在不经意间。

为了让问题变得更加复杂,有时候违反规则反而更好。(一般人我不告诉他!)出来的代码会更干净,甚至可能会更快和更简单。规则通常显得太过于宽泛,有技巧的程序员可以通过打破这些规则来提高代码。不要告诉你的老板,这对你的编码生涯会很有意义。

下面这9个编码习惯,虽然在编程规则中是被驳斥的,但我们很多人就是会不由自主地使用它们。

编程习惯No. 1:使用goto

关于禁止使用goto可以追溯到许多结构化编程工具还未面世的时代。如果程序员想要创建一个循环或跳到另一段程序中,那么他们需要输入goto后再跟一个行号。过了几年之后,编译器团队让程序员使用字符串标签取代行号。这在当时被认为是一个热门的新功能。

有的人认为这会导致“意大利面条式代码”。代码会变得不可读,并且很难理解代码的执行路径。线程混乱,缠缠绵绵到天涯。Edsger Dijkstra就三令五申地表示应该禁止这个命令,他有一份诙谐的手稿,题目为《Goto语句害人不浅》。

但绝对的分支是没有问题的。这就让人纠结了。通常,巧妙的 break 语句和return 语句可提供一个非常干净的关于代码在那个时候执行什么的声明。有时候,添加 goto 到case语句会比更恰当的多级嵌套的if-then-else语句块更易于理解。

也有反例。在苹果的SSL堆栈中的“goto fail”安全漏洞就是最好的例子之一。但是,如果我们能够仔细避免case语句和循环的一些尴尬问题,那么我们就可以嵌入良好的绝对转移,使阅读代码的人更容易明白这是怎么回事。我们可以插入break和return 语句,让每一个人感觉更清洁和更愉快——可能得除了goto的敌视者。

编程习惯No. 2:成功避开文档

我的一个朋友有一个非常精明的老板,这位老板虽然从来没有写过任何代码,但却秉持着每一个功能都必须包含在文档中的理念。哪个程序员不提供注释,那么他就会受到惩罚。所以,我的朋友在他的编辑器中联入了一个有点像人工智能的玩意儿,于是乎,他的每一个功能就都有几行“文档”了。因为这位精明的老板还不够聪明到能理解这些注释其实啥意思也没有,所以我的朋友逃过一劫。他的代码常常被作为正式文档。我想,他应该快要升职了!

许多函数方法,甚至一些类或多或少都能自文档化。冠以insertReservation或cancelReservation或 deleteAll 等名称的函数并不需要多此一举来解释它们的作用。为函数取一个正确的名字往往就足够了。事实上,这比写一段长长的注释要好,因为函数名可以出现在代码中的其他地方。而文档只能默默地呆在某个角落。自文档化的函数名可以改进它们出现的每个文件。

在有些情况下,写文档甚至会导致情况变糟。例如,当代码瞬息万变,团队像疯了似的重构的时候,文档会产生分歧。代码是这样写的,但文档解释的还是四五个版本以前的情况。这类“过时”的文档通常位于代码顶部,有的人会在这里对代码应该发生什么作一个美好总结。因此,尽管重构团队已经仔细修改了相关的注释,但还是会遗漏文件顶部的这段“美好总结”。

当代码和文本出现分歧的时候,注释就变得毫无价值,甚至会产生误导。在这样的情况下,良好的自文档化的代码显然胜出了。

编程习惯No. 3:一行写太多代码

老板突然发神经地给团队发了一封讨厌的邮件:为了执行非常严格的风格规定,我们大家都必须重写我们的代码。最神奇的要求是:每个行为或步骤或子句必须各自成行。你不能使用点语法连续调用函数。在一个分支语句中,你不能有两个及以上返回布尔值的子句。如果要定义变量,那么另起一行。如果你正在做一个复杂的计算,那么不要使用括号。每个片段也自成一行。

他认为他的这个法令将能使调试变得更加容易。就像你单步调试代码一样,调试器会一个动作一个动作地前进。这样就不会卡在某一行。而且更容易执行。

但是这样一来,键盘上的回车键烦不胜烦,因为我需要不断地插入行。而且我敢肯定,老板因此还可以到处吹嘘他的团队能写多少行代码。

唉,有时在同一行中声明一堆变量反而更容易;有时把所有的布尔子句放在一起反而更简单——一切都能变得更加紧凑。那也意味着,我们可以在屏幕上看到更多的逻辑而无需滚动鼠标。更易于阅读就意味着理解起来更快。这才是简单的精粹。

编程习惯No. 4:不声明类型

那些热爱类型化语言的人认为,如果为每个变量添加明确的数据类型声明,就可以写出更好的、没有错误的代码。花一点时间来拼写类型,能帮助编译器在代码开始运行之前标志愚蠢的错误。可能会让人觉得痛苦,但很有帮助。这是编程中停止bug的一种有备无患的方法。

但是时代变了。许多较新的编译器完全可以智能地通过查看代码来推断类型。它们会向后和向前浏览代码,直到可以肯定这个变量是string 还是int,抑或其他。如果这些被查看的类型不成队列,那么错误标志就会点亮。因此再也不需要我们输入变量的类型了。

这意味着我们现在可以在代码中省略掉一些最简单的声明。代码更清洁,而且阅读代码的人也猜得出for循环中命名为 i 的变量表示一个整数型。

编程习惯No. 5:摇摆不定的代码

有的程序员在代码上特别优柔寡断,犹豫不决。先是一开始将值存储为字符串,然后又解析成整数。接着又转换回字符串。这是非常低效的,你甚至可以感觉到CPU在咆哮这种浪费负载的行为。聪明的程序员之所以能快速地编码,是因为他们事先会设计架构,以尽量减少转换。他们的代码能更快地运行是因为他们有一个良好的规划。

但是,不管你信不信,这种摇摆不定的代码有时候也是有意义的。比如说,你有一个非常棒的库,在它专有的黑盒子里能做无数智能的事情。如果库需要字符串的数据,那么你就给它字符串,即使你刚将这个数据转换成为整数型。

当然,你可以重写所有的代码,以尽量减少转换,但是这需要时间。而且,有时候让代码稍微多花点额外时间来运行也未尝不可,因为重写代码需要耗费我们更多的时间。有时,背负这样的技术债务比一开始就正确构建的成本要更低。

有的时候,库不是专有的代码,但那些你以前全部自己写的代码是你独有的。有的时候,再次转换数据比重写库中的所有代码要快得多。所以,就让它这样吧,就让代码摇摆吧。

编程习惯No. 6:编写你自己的数据结构

有一个标准规则是,程序员在完成数据结构课程的第二年,不应该写用于存储数据的代码。基本上我们需要的所有的数据结构,已经有人写好了,而且其代码已历经多年的测试和再测试。它和语言捆绑在一起,而且常常是免费的。你的代码只能造就bug。

但有时你会发现数据结构库有点慢。有时它们会迫使我们使用标准的,但于我们的代码却是错误的结构。有时库会把我们推向在使用结构之前重新配置数据的地步。有时库会包含一些所谓有备无患的保护功能,如线程锁,但其实我们的代码并不需要。

如果遇到这种情况,那么就应该着手写我们自己的数据结构。这或许能让你做得更快,做得更多。而且代码会变得更清洁,因为我们不会包括那些多余的用于格式化数据来完成一些功能的代码。

编程习惯No. 7:在中间打破循环

有一个规则制定小组宣称,每个循环都应该有一个“常量”,也就是说当这个逻辑语句为true的时候,循环一直执行。当常量一定不会是true的时候,循环才会结束。这是考虑复杂循环的好方法,但它会导致愚蠢的禁令——例如禁止我们在循环中间使用return 和break 语句。这一条也包含在禁止goto语句的规则中。

这个理论是好的,但它通常会导致更复杂的代码。请看下面这个简单的案例,遍历数组,将找到的元素传递给test函数,并将该元素返回:

1
2
3
4
5
while (i<a.length){ 
...
if (test(a[i]) then return a[i];
...
}

“循环常量”爱好者会要求我们增加一个布尔变量,命名为notFound,然后这样使用:

1
2
3
4
5
while ((notFound) && (i<a.length){ 
...
if (test(a[i])) then notFound=false;
...
}

如果这个布尔值能够合理地命名,那么这就是一段很棒的自文档化的代码,更易于大家理解。但这也增加了复杂性。这意味着你需要分配另一个局部变量,并堵塞寄存器,因为编译器也许还不能足够智能到解决这个问题。

有时候,一个goto 语句或一个跳转会更干净利索。

编程习惯No. 8:使用短变量名(i和x和and也是有意义的)

Edgar Allan Poe这位诗人和小说家曾经说过,在一个故事中的每一个词都应该是有内涵的。编码规则也强调如此。变量名应该说明这个变量的所作所为。那些使用驼峰式大小写的方法来写变量名,以表达关于变量细节的Java程序员深以为然,于是一个又一个疯狂长度的变量名出炉了。有些程序员写的变量名,会组合五六个甚至更多的词语。

但有的时候,使用单个字母作为变量名反而会更方便。有时在循环迭代中只使用i或j会更简单。有时使用字母a代表array ,l代表list会更便捷,即使是字母l和数字1看上去很难辨别。

正如这篇文章前面鼓励的是自文档化的代码,而非长长的注释。在上述情况下,单个字母的变量名也是自文档化的。字母 i 是通用的迭代器。只要是程序员立刻就会懂。

编程习惯No. 9:重新定义运算符和函数

一些最有趣的编程语言允许你去做一些特别诡异的事情,例如重新定义元素的值,就如同常量一般。例如Python,你可以输入TRUE=FALSE(在Version2.7及之前的版本)。这并不会产生某种逻辑崩溃,或导致宇宙终结——仅仅只是互换了TRUE和FALSE的含义。你也可以在C预处理器和一些其他语言中玩玩类似于这样的危险游戏。还有一些语言允许你重新定义运算符,如加号。

当然这是延伸了,不过有一个观点是,在一个大的代码块内,当重新定义一个或多个所谓的常量时,速度会更快。有时老板会要求代码做一些截然不同的事情。当然,你可以修改代码的每个事件,或者,你可以重新定义。这让你看上去像一个天才。不必重写一个庞大的库,只需翻转一下,就可以做相反的事情了。

这9个习惯就都在这儿了。千万不要轻易尝试,不管它看上去有多牛掰。太危险了——真的,这是实话。

JVM内存配置参数说明

Posted on 2018-03-01 | In Java核心技术 |

JVM内存划分

Xms -Xmx分别设置堆的最小值和最大值,如果要设置成堆的大小可变,那么可以将最大值和最小值设置成不一样,如果要将堆大小固定,那么只需将最大值和最小值设置成一样的就行。
jvm中分为堆和方法区,堆又进一步分为新生代和老年代。方法区为永久代,堆中区分的新生代和老年代是为了垃圾回收,新生代中的对象存活期一般不长,而老年代中的对象存活期较长,所以当垃圾回收器回收内存时,新生代中垃圾回收效果较好,会回收大量的内存,而老年代中回收效果较差,内存回收不会太多。

基于以上特性,新生代中一般采用复制算法,因为存活下来的对象是少数,所需要复制的对象少,而老年代对象存活多,不适合采用复制算法,一般是标记整理和标记清除算法。因为复制算法需要留出一块单独的内存空间来以备垃圾回收时复制对象使用,所以将新生代分为eden区和两个survivor区,每次使用eden和一个survivor区,另一个survivor作为备用的对象复制内存区。

所谓的 Copying算法 是空间换时间,而 Mark-Compact算法 则是时间换空间。因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法( Copying算法 )。
Copying算法: 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
Mark-Compact算法: 在工作的时候则需要分别的mark与compact阶段,mark阶段用来发现并标记所有活的对象,然后compact阶段才移动对象,清楚未标记对象来达到compact的目的。如果compact方式是sliding compaction,则在mark之后就可以按顺序一个个对象“滑动”到空间的某一侧。因为已经先遍历了整个空间里的对象图,知道所有的活对象了,所以移动的时候就可以在同一个空间内而不需要多一份空间。

常见配置汇总

  • 堆设置
    • Xms:初始堆大小
    • Xmx:最大堆大小
    • XX:NewSize=n:设置年轻代大小
    • XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
    • XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
    • XX:MaxPermSize=n:设置持久代大小
  • 收集器设置
    • XX:+UseSerialGC:设置串行收集器
    • XX:+UseParallelGC:设置并行收集器
    • XX:+UseParalledlOldGC:设置并行年老代收集器
    • XX:+UseConcMarkSweepGC:设置并发收集器
  • 垃圾回收统计信息
    • XX:+PrintGC
    • XX:+PrintGCDetails
    • XX:+PrintGCTimeStamps
    • Xloggc:filename
  • 并行收集器设置
    • XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
    • XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
    • XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
  • 并发收集器设置
    • XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
    • XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

Java中数组复制的效率比较

Posted on 2018-02-28 | In Java核心技术 |

在开发中,数组复制是经常使用的,很多方法都可以进行数组赋值,但是效率却天差地别:效率最高的是:System.arraycopy(), 下面是它的使用方式的参数说明:

我们可以看看它的源代码,它是个native方法,毫无疑问效率最高:

再说说Arrays.copyof()方法,看源代码发现,它还是调用了System.arraycopy()方法:

然后呢,再看看Object类的clone方法:

clone()的返回值是Object类型,强制类型转换毫无疑问是降低了效率,但是好歹是native方法,不会存在有特别明明显的差距的。当然自己通过for循环的方式也可以进行数组的复制,但是效率依旧是很低的!所以还是推荐用System.arraycopy() 来进行数组的复制吧!

网页启动本地Activity

Posted on 2018-02-13 | In 移动开发 |

前言

Intent这个类在开发中是很常用的类,代表了着一个意图(获取理解为目标、目的),首先我们需要明确一点的就是:任何一个浏览器链接都是一个隐式意图,打开一个浏览器的方式无非就是显式意图和隐式意图,所以我们配置过滤器即可!

示例

首先,工程目录如图:

MainActivity和布局文件都不用改,关键是manifests文件中LocalAppAty的配置:

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.xpu.launchlocalapp">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".LocalAppAty" android:label="LocalAppAty">
<intent-filter>
<!--可以被浏览器启动的Activity-->
<category android:name="android.intent.category.BROWSABLE"></category>
<category android:name="android.intent.category.DEFAULT"></category>

<action android:name="android.intent.action.VIEW"></action>
<data android:scheme="app" ></data>
</intent-filter>
</activity>
</application>
</manifest>

JavaWeb工程如下,一个很简单的标签:

一般安卓模拟器要访问本机的ip地址,使用10.0.0.2,端口号还是与你的服务器一致,我的是8080:

成功开启:

同时获取到了启动该Activity的信息来源:

Ajax异步请求与JSON数据格式

Posted on 2017-12-21 | In 前端 |

百度的预搜索是怎么实现的呢?如下图:


这个场景应该是大家非常熟悉的吧,为什么我们没有点击搜索但是却可以弹出相关的搜索内容条目呢?其中就用到了ajax引擎!接下来我们就可以看一下这个ajax,哈哈!

一、Ajax概述

什么是同步,什么是异步

同步现象:客户端发送请求到服务器端,当服务器返回响应之前,客户端都处于等待卡死状态
异步现象:客户端发送请求到服务器端,无论服务器是否返回响应,客户端都可以随 意做其他事情,不会被卡死

Ajax的运行原理

页面发起请求,会将请求发送给浏览器内核中的Ajax引擎,Ajax引擎会提交请求到 服务器端,在这段时间里,客户端可以任意进行任意操作,直到服务器端将数据返回 给Ajax引擎后,会触发你设置的事件,从而执行自定义的js逻辑代码完成某种页面功能。

二、js原生的Ajax技术

js原生的Ajax其实就是围绕浏览器内内置的Ajax引擎对象进行学习的,要使用js原生的Ajax完成异步操作,有如下几个步骤:

  • 创建Ajax引擎对象
  • 为Ajax引擎对象绑定监听(监听服务器已将数据响应给引擎)
  • 绑定提交地址
  • 发送请求

下面是一个使用原生Ajax的示例:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
//异步请求
function fn1(){
//(1)创建引擎对象
var xmlhttp = new XMLHttpRequest();

//(2)绑定监听
xmlhttp.onreadystatechange = function(){
//(5)接受相应数据
if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
var res = xmlhttp.responseText;
document.getElementById("span1").innerHTML = res;
}
}

//(3)绑定地址
xmlhttp.open("GET", "/WEB21/ajaxservlet", true);

//(4)发送请求
xmlhttp.send();
}

//同步请求
function fn2(){
//(1)创建引擎对象
var xmlhttp = new XMLHttpRequest();

//(2)绑定监听
xmlhttp.onreadystatechange = function(){
//(5)接受相应数据
if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
var res = xmlhttp.responseText;
document.getElementById("span2").innerHTML = res;
}
}

//(3)绑定地址
xmlhttp.open("GET", "/WEB21/ajaxservlet", false);

//(4)发送请求
xmlhttp.send();
}
</script>
</head>
<body>
<input type="button" value="异步访问服务器" onclick="fn1()"/><span id="span1"></span>
<br>
<input type="button" value="同步访问服务器" onclick="fn2()"/><span id="span2"></span>
<br>
<input type="button" value="测试按钮" onclick="alert()"/>
</body>
</html>

servlet如下:

1
2
3
4
5
6
7
8
9
10
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().write("XPU");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write(Math.random()+"");
}

这样的话会得到下图所示的效果:

现象很简单,我的测试按钮其实就是弹出一个空的提示框,当我点击异步访问的时候,浏览器会向服务器的servlet发送一个请求,这个请求是获取一个随机数,而且为了模拟服务器长达3秒的运算我让服务器睡眠了3秒钟,点击异步访问后可以立马点击测试按钮弹出提示框,但是点击同步访问后立马点击测试按钮却要等待3秒才会弹出提示框,而且我点击了多次就弹出了多次提示框,相信这个额例子是非常容易理解同步和异步的特点的!

为什么要这样做呢?有时候我们在加载网页的时候,可能有些图片非常大,在网速不是很好的情况下需要很长的时间才可以加载,如果使用ajax引擎发起对图片的异步请求,即使在图片还没有加载完毕的情况下也可以使用其他的功能,这就是异步请求的一个应用,接下来用图片说明一下:

很显然,如果没有ajax引擎的情况下发起请求而且等到收到响应对于浏览器来说是非常浪费时间的,尤其是发起的请求计算量过大,网速特别慢的时候是非常影响用户体验的,有了ajax引擎替我们发起请求和接受协议,浏览器就有机会去做其他的事情,而不是傻傻的等待!

原生ajax的GET与POST请求:
GET 请求比较简单,如下格式即可(上面的代码中用的就是GET请求)

1
2
xmlhttp.open("GET","test1.txt",true);
xmlhttp.send();

但是使用GET请求可能的到的是缓存结果,为了避免这样的情况出现,应该使用如下示例设置不同的ID:

1
2
xmlhttp.open("GET","demo_get.asp?t=" + Math.random(),true);
xmlhttp.send();

简单地POST请求:

1
2
xmlhttp.open("POST","demo_post.asp",true);
xmlhttp.send();

带参数的POST请求(切记不要忘记添加请求头):

1
2
3
xmlhttp.open("POST","ajax_test.asp",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("fname=Bill&lname=Gates");

最后说一个关于获取XMLHttpRequest 对象的问题:
创建 XMLHttpRequest 对象:所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。

XMLHttpRequest 对象三个重要的属性

属性 描述
onreadystatechange 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
readyState 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。0: 请求未初始化 1: 服务器连接已建立 2: 请求已接收 3: 请求处理中 4: 请求已完成,且响应已就绪
status 200: “OK” 404: 未找到页面

创建 XMLHttpRequest 对象的语法:

1
variable=new XMLHttpRequest();

老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象:

1
variable=new ActiveXObject("Microsoft.XMLHTTP");

为了应对所有的现代浏览器,包括 IE5 和 IE6,请检查浏览器是否支持 XMLHttpRequest 对象。如果支持,则创建 XMLHttpRequest 对象。如果不支持,则创建 ActiveXObject :

1
2
3
4
5
6
7
8
var xmlhttp;
if (window.XMLHttpRequest){
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}else{
// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

使用XMLHttpRequest对象用于在后台与服务器交换数据,应用场景:

  • 在不重新加载页面的情况下更新网页
  • 在页面已加载后从服务器请求数据
  • 在页面已加载后从服务器接收数据
  • 在后台向服务器发送数据

所有异步访问都是靠ajax引擎!

三、JSON数据格式

json是一种与语言无关的数据交换的格式,作用:

  • 使用ajax进行前后台数据交换
  • 移动端与服务端的数据交换

Json的格式与解析

json有两种格式:
1)对象格式:{“key1”:obj,”key2”:obj,”key3”:obj…}
2)数组/集合格式:[obj,obj,obj…]

例如:user对象 用json数据格式表示

1
{"username":"zhangsan","age":28,"password":"123","addr":"北京"}

例如:List 用json数据格式表示

1
[{"pid":"10","pname":"小米4C"},{},{}]

注意:对象格式和数组格式可以互相嵌套
注意:json的key是字符串 jaon的value是Object

json的解析:
json是js的原生内容,也就意味着js可以直接取出json对象中的数据

Json的转换插件

将java的对象或集合转成json形式字符串

json的转换插件是通过java的一些工具,直接将java对象或集合转换成json字符串。
常用的json转换工具有如下几种:
1)jsonlib
2)Gson:google
3)fastjson:阿里巴巴
4)cJSON:腾讯的

四、Jquery的Ajax技术

jquery是一个优秀的js框架,自然对js原生的ajax进行了封装,封装后的ajax的操 作方法更简洁,功能更强大,与ajax操作相关的jquery方法有如下几种,但开发中经常使用的有三种 :

1
2
$.get(url, [data], [callback], [type])
$.post(url, [data], [callback], [type])

url:代表请求的服务器端地址
data:代表请求服务器端的数据(可以是key=value形式也可以是json格式)
callback:表示服务器端成功响应所触发的函数(只有正常成功返回才执行)
type:表示服务器端返回的数据类型(jquery会根据指定的类型自动类型转换)
常用的返回类型:text、json、html等

1
$.ajax( { option1:value1,option2:value2... } );

async:是否异步,默认是true代表异步
data:发送到服务器的参数,建议使用json格式
dataType:服务器端返回的数据类型,常用text和json
success:成功响应执行的函数,对应的类型是function类型
type:请求方式,POST/GET
url:请求服务器端地址

下面是一个使用示例:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
<script type="text/javascript">
function fn1() {
//get异步访问
$.get(
"/WEB21/ajaxservlet",//url地址
{"name":"邹长林","age":20},//请求参数
function(data){ //成功后的回调函数
alert(data.firstname+" "+data.lastname+" "+data.age);
},
"json"
);
}

function fn2() {
$.post(
"/WEB21/ajaxservlet",//url地址
{"name":"邹长林","age":20},//请求参数
function(data){ //成功后的回调函数
alert(data.firstname+" "+data.lastname+" "+data.age);
},
"json"
);
}

function fn3() {
$.ajax({
url:"/WEB21/ajaxservlet", //请求的地址
type:"GET", //请求类型
async:true, //是否同步,默认同步
data:{"name":"tim", "age":18}, //请求的数据,JSON格式
success:function(data){ //请求成功的回调函数
alert(data.firstname);
},
error:function(){ //请求失败的回调函数
alert("请求失败");
},
dataType:"json",//从服务器接受返回的数据类型,一般为JSON或者text

});
}
</script>
<body>
<input type="button" value="GET访问服务器" onclick="fn1()"/>
<span id="span1"></span>
<br>
<input type="button" value="POST访问服务器" onclick="fn2()"/>
<span id="span2"></span>
<br>
<input type="button" value="Ajax访问服务器" onclick="fn3()"/>
</body>
</html>

在使用Ajax三种请求的时候需要注意的地方:
GET方式提交的数据到服务器可能会出现乱码,使用编解码的方式就可以解决
POST方式提交的数据已经经过ajax处理了,无需我们再自己处理一遍
获取数据的时候的乱码问题也是很好解决的:

1
request.setCharacterEncoding("UTF-8");

忍受简单的能力

Posted on 2017-12-07 | In 杂谈 |

我关上灯,对女儿说:「闭上眼睛,别乱动了。」

女儿立刻大声抗议:「可是我睡不着!」

我只好又强调了一遍:「我只是请你闭上眼睛,别乱动。」

我从来没说过「请你尽快睡着」,那是我女儿的脑补。我当然挺高兴的,她能脑补出那句话来,说明她起码脑子不笨,能够猜到一个指令之后的真实意图。但同时我也有担心。因为这一点聪明,她入睡可能就会困难一点。「闭上眼睛,别乱动」是一条很简单的指令,是我认为她充分有能力做到的。但她不安于这条指令,而去考虑「即便我照着做了,我可能还是睡不着」。这样的思考,对当下有害无益。

同样的事情在我的工作中也常常遇到。比如说,我跟学生辅导员讲,危机干预中哪些危险的信号是需要注意的。有的辅导员就会问:「可是李老师,有的学生其实有危险的想法,但他就是憋着不说,也根本不表达这些信号,我们怎么去识别呢?」我说:「那是另外一种情况了,但我刚刚讲的不是这种情况,我刚刚讲的你都记住了吗?」他说那都很简单。我说:「请你复述一遍?」结果他可能说不上来。

他们的注意力都集中在「要是这种方法不行呢?」,反而忽略了对「这种方法」本身的吸收。其实,我教的是更简单,更常见的情形,从现实性来讲,比他们考虑的那些例外情况更重要,也更有开展工作的空间。可以说他们是因小失大。

我做咨询的时候,有时候要教来访者尝试一些不一样的说话方式。比如,用更坚定的语气表达拒绝。但是教完之后,他们常常不能真的付诸实践。下一次咨询的时候他们不说自己练习时遇到的困难,而是深入思考:「万一」对方根本不听我的,「万一」对方如何如何纠缠,「万一」对方表现得更强硬,又该怎么办?……假如我们就这些话题展开讨论,完全还可以讨论十次二十次。脑子更快的人,甚至一听我讲完就忧心忡忡地想到:「要是一直拒绝别人的要求,以后会不会就没朋友了?」

他距离「一直拒绝别人的要求」还远得很呢,但他已经在担心了。

老师都喜欢教聪明的学生,因为他们脑子反应很快,就可以省很多时间。但是太聪明了也不好。因为脑子太快了,需要身体用工夫的地方,就有种种困难。

聪明是在头脑中加速的过程。当我匀速前进的时候,聪明的孩子就在思考:他下一步会走向哪里?你看,我明明还在这一步,但是在聪明人眼里,下一步等于已经有了。他们思考的速度,快于我实际的步速。到我真的在走下一步时,他们的想法也许已经发展为:「这条路通向何方?」他们绝不会满足于跟随我的步伐,而要直接预见到我的终点。再然后,他们会猜测:为什么要到那里?到了之后又会如何?今天还有什么其它打算?这种思维的推进,大刀阔斧,我的路还没走到一半,他们在脑子里说不定已经演绎完了我的一天。换到上课的情境,就是我刚说了上半句,学生就已然猜到了下半句,可能就连一堂课要讲的全部内容,都落到了他们的预知之内。

据我所知,这样的学生上课很容易走神……

对于聪明人来说,最难以忍受的情况不是一件事有多难,而是纯粹的简单。没有难度挑战的任务,会让他们感到无所着力,继而注意涣散,不得已靠着「举一反三」之类的小花样来自我提神。重复的练习是他们的死穴。——你去问一个健身教练,他多半就见过不少这样的客户:他们一个动作只要重复一两遍,就会开始琢磨:「这个练习真的管用吗?」「这里面真正关键的元素在哪里?」「练完这个,下一步练什么?」借着这些天马行空的思考,他们才能松口气,从当下的枯燥中解脱出来。

而思维上的变化多端,就造成了行动层面的进步迟缓。

就拿我女儿的例子来说,「闭上眼睛,别乱动」是她入睡的第一步,而「睡着」则是第N步。她在第一步的阶段担心第N步的结果,反而连第一步也做不到。

所以我认识的学生里面,除了少部分天赋异禀的奇才之外,真正最影响一个人的成就的因素,可能不是智商,也不是努力,而在于他有多「踏实」。踏实的人做一件事,是一件事;学一样东西,就学得到一样东西。你只要看一门课最开始的时候,讲一些最简单的知识,哪些人可以不厌其烦地听进去,他们未来就算没有什么惊人的成就,也都不会混得太差。而聪明人往往已失去了耐心,都趴在桌子上睡觉。

骐骥一跃,不能十步;驽马十驾,功在不舍。

但趴在桌子上睡觉,还不算是最糟糕的学习状态。我自己上课时也睡过无数,就我的经验来说,当然什么也没学到,但起码知道没学到东西。更可怕的情况是自我催眠,感觉自己在学,实则空空如也。一种典型的催眠方式,就是用手机把每一页PPT都拍下来,之后该开小差照样开小差。他们以为自己「学到了」,但无非是在手机里储存了一堆只在考试前才会看一遍的照片而已。有时我会禁止学生照相,但他们还是会把电脑搬到教室里。一边听课,一边噼里啪啦地打字。这样也算很努力。但他们努力把课堂的内容敲进电脑里,就是为了自己可以更心安理得地记不住它们。

之所以说这种情况更可怕,是因为他们运用了不露痕迹的方式,把「并没有真的学到什么东西」这件事巧妙地敷衍过去:「反正随时可以再看我的笔记。」

这样的人也许会买很多书(然而不看),或者读很多文章(然而不想),或者整理出很多读书心得(然而并不用来改变自己)。这些事做得越卖力,他们陷入的幻觉可能就越深。有一些读者常常给我留言:「你说的没错,可是然后呢?」你看,他们关注的不是我说了什么,而是我没说的还有哪些。描述一种现象,他们首先会想到:可是也有例外吧?如果证明是一个普适的规律,他们又会说:原因是什么呢?假设提出了原因,他们很快又抱怨:说得头头是道,怎么不讲一讲解决办法?如果我有那么牛,连解决建议都提了,恐怕还是会说:道理都懂,然而并没有什么X用。

这种思维方式,就等于是说:「我们来聊一聊A吧。」

「好啊,我最近认识了A的朋友B,B是个好人,他还介绍我认识了C……」

你看,这种思维方式自有好处。话题已经从A的身上转开了,但是在说话的人看来,似乎仿佛,自己并不算是在跑题。正如「道理都懂,然而并没有什么X用」这句话,说出来很轻松,也就不会让人觉察到——其实道理也没有真的都懂。

我想,这里面大概也有一种安全感。一个人学东西之所以无法专注,可能就是因为他无法忍受专注在一个点上的感觉。学习一个东西(尤其是简单的,重复的)往往让人焦虑。因为这一刻你真的停在一个东西上,就会意识到自己有多渺小,而要学的东西似乎还无穷无尽。因此,用最快的速度跳跃式地前进,用摘要的形式纵观大概,存成照片或者写成笔记,或是把注意力投向这个东西之外,「然后呢,然后还有什么?」这样就可以说:「行了,这个我已经懂了。」这是回避焦虑的法宝。

一口一口地吃饭太慢了。恨不得一口吃下一百口,谁叫锅里还有那么多?

所以重要的事情才要说三遍。可是你还记得上一段看了三遍的话是什么吗?

对一个学习者来说,这个时代是最好的时代,但可能也是最坏的时代。今天的信息是整个地泛滥了,你很难让自己真的不去焦虑。如果你想用方便的方式解决这种焦虑,就只有不断吸收复杂的信息。并不缺这样的信息源,随便在网上找一找,就有数不清的「绝世武功的目录」,这辈子肯定练不完,只好先用脑子过一遍。

如果真的想学一点东西,就需要一种特别的能力。我把它叫做「忍受简单的能力」。我不知道是叫能力还是勇气更好,因为它涉及到了一种真正意义上的放弃。——当你在某一个点上停下来,打算认真下点工夫的时候,这意味着放弃想象中的其它可能。你得到的只是简单的一点点,失去的却是头脑中的整片汪洋。一个人守着这样一点,面对巨大的不确定也不逃避,他要么需要很勇敢,要么是很天真。

就像一个专注吃手的婴儿,他一旦意识到自己离长大还有多远,可能就急了。

这篇文章阐述的也不过是一个简单的道理而已,几句话就能说明白,并没有给出什么成体系的理论,方法,和建议。然后呢?——然后我就停在这里了。

原文地址:《忍受简单的能力》

下载中文文件乱码解决方式

Posted on 2017-11-04 | In Java核心技术 |

关于编码的问题有几点需要说清楚:UTF-8国际编码,GBK中文编码。GBK包含GB2312,即如果通过GB2312编码后可以通过GBK解码,反之可能不成立;这个道理很简单,计算机存储的是010001010010…这种的数据,也只能存储这样的数据,通过二进制的规则可以解析为数字,如2二进制就是10,这也就意味着任意数字在有限存储位的情况下都可表示为010101…这样的数据,老外的文字就是26个字母,我们假想为分别对应1~26,但是却不是这样的,真正的对应关系就是ASCII码表中的关系,但是如何表示汉字呢?很显然也需要一套对应的码表,于是UTF-8、GBK、GB2312这些编码方式就是为了解决这个问题的。

下面看看正题:首先我的目录是:压缩包即是我要下载的文件

我的下载界面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3> 使用a标签指向服务器资源 </h3>
<a href="/Web14_response/download/a.flv">a.flv</a><br>
<a href="/Web14_response/download/a.jpg">a.jpg</a><br>
<a href="/Web14_response/download/a.mp3">a.mp3</a><br>
<a href="/Web14_response/download/a.mp4">a.mp4</a><br>
<a href="/Web14_response/download/a.txt">a.txt</a><br>
<a href="/Web14_response/download/a.zip">a.zip</a><br>
<h3> 使用服务器代码完成服务器资源下载 </h3>
<a href="/Web14_response/DownloadServlet2?filename=a.flv">a.flv</a><br>
<a href="/Web14_response/DownloadServlet2?filename=a.jpg">a.jpg</a><br>
<a href="/Web14_response/DownloadServlet2?filename=a.mp3">a.mp3</a><br>
<a href="/Web14_response/DownloadServlet2?filename=a.mp4">a.mp4</a><br>
<a href="/Web14_response/DownloadServlet2?filename=a.txt">a.txt</a><br>
<a href="/Web14_response/DownloadServlet2?filename=压缩包.zip">压缩包.zip</a><br>
</body>
</html>

提供文件下载的Servlet:

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
package com.xpu.content;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sun.misc.BASE64Encoder;

public class DownloadServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 这个主要是下载中文的
// 获取要下载的文件的名称
String filename = request.getParameter("filename");
// 解决获得中文参数的乱码
filename = new String(filename.getBytes("ISO8859-1"), "UTF-8");
// 设置头信息
response.setContentType(getServletContext().getMimeType(filename));
String agent = request.getHeader("User-Agent");
String filenameEncoder = "";
if (agent.contains("MSIE")) {
// IE浏览器
filenameEncoder = URLEncoder.encode(filename, "utf-8");
filenameEncoder = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filenameEncoder = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filenameEncoder = URLEncoder.encode(filename, "utf-8");
}

response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);
System.out.println(filename);
// 获取文件的绝对路径
String path = getServletContext().getRealPath("/download/" + filename);
// 获取该文件的输入流
InputStream is = new FileInputStream(path);
// 获取输出流
ServletOutputStream os = response.getOutputStream();
// 文件拷贝的模板代码
int len = 0;
byte[] bys = new byte[1024];
while ((len = is.read(bys)) != -1) {
os.write(bys, 0, len);
}
is.close();
}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

下载效果:

1…678

Tim

IT技术发烧友,喜爱新事物!如何让一滴水不干涸?只有把它融入大海。

74 posts
19 categories
1 tags
RSS
GitHub E-Mail StackOverflow
Friends
  • LiLiLiLaLa
  • Ahoj
  • MrDalili
  • Rowe98
  • Andrew
  • Lrsand52m
  • 4559的博客
  • JinLiGithub
  • Hansionz
  • Mr-Hunter
© 2019 Copyright Tim
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4 陕ICP备19008567号