首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

Python爬虫抓取智联招聘(基础版)

运行平台:WindowsPython版本:Python3.6IDE:Sublime Text其他工具:Chrome浏览器 1、网页分析 1.1 分析请求地址 以北京海淀区的python工程师为例进行网页分析。打开智联招聘首页,选择北京地区,在搜索框输入"python工程师",点击"搜工作": 想学习可以加Python学习q-u-n-227-435-450 即可获取,内附:开发工具和安装包,以及系统学习路线图,接下来跳转到搜索结果页面,按"F12"打开开发者工具,然后在"热门地区"栏选择"海淀",我们看一下地址栏: 由地址栏后半部分searchresult.ashx?jl=北京&kw=python工程师&sm=0&isfilter=1&p=1&re=2005可以看出,我们要自己构造地址了。接下来要对开发者工具进行分析,按照如图所示步骤找到我们需要的数据:Request Headers和Query String Parameters: 构造请求地址: paras = {'jl': '北京', # 搜索城市'kw': 'python工程师', # 搜索关键词 'isadv': 0, # 是否打开更详细搜索选项'isfilter': 1, # 是否对结果过滤'p': 1, # 页数're': 2005# region的缩写,地区,2005代表海淀}url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?' + urlencode(paras) 请求头: headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36','Host': 'sou.zhaopin.com','Referer': 'https://www.zhaopin.com/','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','Accept-Encoding': 'gzip, deflate, br','Accept-Language': 'zh-CN,zh;q=0.9'} 1.2 分析有用数据 接下来我们要分析有用数据,从搜索结果中我们需要的数据有:职位名称、公司名称、公司详情页地址、职位月薪: 通过网页元素定位找到这几项在HTML文件中的位置,如下图所示: 用正则表达式对这四项内容进行提取: # 正则表达式进行解析pattern = re.compile('(.*?).*?' # 匹配职位信息 '(.*?).*?' # 匹配公司网址和公司名称 '(.*?)', re.S) # 匹配月薪 # 匹配所有符合条件的内容items = re.findall(pattern, html) 注意:解析出来的部分职位名称带有标签,如下图所示: 那么在解析之后要对该数据进行处理剔除标签,用如下代码实现: for item in items: job_name = item[0] job_name = job_name.replace('', '') job_name = job_name.replace('', '')yield {'job': job_name,'website': item[1],'company': item[2],'salary': item[3] } 2、写入文件 我们获取到的数据每个职位的信息项都相同,可以写到数据库中,但是本文选择了csv文件,以下为百度百科解释: 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。 由于python内置了csv文件操作的库函数,所以很方便: import csvdefwrite_csv_headers(path, headers):''' 写入表头 '''with open(path, 'a', encoding='gb18030', newline='') as f: f_csv = csv.DictWriter(f, headers) f_csv.writeheader()defwrite_csv_rows(path, headers, rows):''' 写入行 '''with open(path, 'a', encoding='gb18030', newline='') as f: f_csv = csv.DictWriter(f, headers) f_csv.writerows(rows) 3、进度显示 要想找到理想工作,一定要对更多的职位进行筛选,那么我们抓取的数据量一定很大,几十页、几百页甚至几千页,那么我们要掌握抓取进度心里才能更加踏实啊,所以要加入进度条显示功能。 本文选择tqdm 进行进度显示,来看一下酷炫结果(图片来源网络): 执行以下命令进行安装:pip install tqdm。 简单示例: from tqdm import tqdmfrom time import sleepfor i in tqdm(range(1000)): sleep(0.01) 4、完整代码 以上是所有功能的分析,如下为完整代码: #-*- coding: utf-8 -*-import reimport csvimport requestsfrom tqdm import tqdmfrom urllib.parse import urlencodefrom requests.exceptions import RequestExceptiondefget_one_page(city, keyword, region, page):''' 获取网页html内容并返回 ''' paras = {'jl': city, # 搜索城市'kw': keyword, # 搜索关键词 'isadv': 0, # 是否打开更详细搜索选项'isfilter': 1, # 是否对结果过滤'p': page, # 页数're': region # region的缩写,地区,2005代表海淀 } headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36','Host': 'sou.zhaopin.com','Referer': 'https://www.zhaopin.com/','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','Accept-Encoding': 'gzip, deflate, br','Accept-Language': 'zh-CN,zh;q=0.9' } url = 'https://sou.zhaopin.com/jobs/searchresult.ashx?' + urlencode(paras)try:# 获取网页内容,返回html数据 response = requests.get(url, headers=headers)# 通过状态码判断是否获取成功if response.status_code == 200:return response.textreturnNoneexcept RequestException as e:returnNonedefparse_one_page(html):''' 解析HTML代码,提取有用信息并返回 '''# 正则表达式进行解析 pattern = re.compile('(.*?).*?'# 匹配职位信息' ', re.S) # 匹配月薪 # 匹配所有符合条件的内容 items = re.findall(pattern, html) for item in items: job_name = item[0] job_name = job_name.replace('', '') job_name = job_name.replace('', '')yield {'job': job_name,'website': item[1],'company': item[2],'salary': item[3] }defwrite_csv_file(path, headers, rows):''' 将表头和行写入csv文件 '''# 加入encoding防止中文写入报错# newline参数防止每写入一行都多一个空行with open(path, 'a', encoding='gb18030', newline='') as f: f_csv = csv.DictWriter(f, headers) f_csv.writeheader() f_csv.writerows(rows)defwrite_csv_headers(path, headers):''' 写入表头 '''with open(path, 'a', encoding='gb18030', newline='') as f: f_csv = csv.DictWriter(f, headers) f_csv.writeheader()defwrite_csv_rows(path, headers, rows):''' 写入行 '''with open(path, 'a', encoding='gb18030', newline='') as f: f_csv = csv.DictWriter(f, headers) f_csv.writerows(rows)defmain(city, keyword, region, pages):''' 主函数 ''' filename = 'zl_' + city + '_' + keyword + '.csv' headers = ['job', 'website', 'company', 'salary'] write_csv_headers(filename, headers)for i in tqdm(range(pages)):''' 获取该页中所有职位信息,写入csv文件 ''' jobs = [] html = get_one_page(city, keyword, region, i) items = parse_one_page(html)for item in items: jobs.append(item) write_csv_rows(filename, headers, jobs)if __name__ == '__main__': main('北京', 'python工程师', 2005, 10) (.*?).*?'# 匹配公司网址和公司名称'(.*?) 上面代码执行效果如图所示: 执行完成后会在py同级文件夹下会生成名为:zl_北京_python工程师.csv的文件,打开之后效果如下:

优秀的个人博客,低调大师

Java基础9:解读Java回调机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a724888/article/details/80100185 这位大侠,这是我的公众号:程序员江湖。 分享程序员面试与技术的那些事。 干货满满,关注就送。 本文主要介绍了Java中的回调机制,以及Java多线程中类似回调的机制。 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl.github.io/2018/04/26/javase9 更多关于Java后端学习的内容请到我的CSDN博客上查看:https://blog.csdn.net/a724888 模块间的调用 本部分摘自https://www.cnblogs.com/xrq730/p/6424471.html 在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种: (1)同步调用 同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b(),一直等待b()方法执行完毕,a()方法继续往下走。这种调用方式适用于方法b()执行时间不长的情况,因为b()方法执行时间一长或者直接阻塞的话,a()方法的余下代码是无法执行下去的,这样会造成整个流程的阻塞。 (2)异步调用 异步调用是为了解决同步调用可能出现阻塞,导致整个流程卡住而产生的一种调用方式。类A的方法方法a()通过新起线程的方式调用类B的方法b(),代码接着直接往下执行,这样无论方法b()执行时间多久,都不会阻塞住方法a()的执行。 但是这种方式,由于方法a()不等待方法b()的执行完成,在方法a()需要方法b()执行结果的情况下(视具体业务而定,有些业务比如启异步线程发个微信通知、刷新一个缓存这种就没必要),必须通过一定的方式对方法b()的执行结果进行监听。 在Java中,可以使用Future+Callable的方式做到这一点,具体做法可以参见我的这篇文章Java多线程21:多线程下其他组件之CyclicBarrier、Callable、Future和FutureTask。 (3)回调 最后是回调,回调的思想是: 类A的a()方法调用类B的b()方法 类B的b()方法执行完毕主动调用类A的callback()方法 这样一种调用方式组成了上图,也就是一种双向的调用方式。 回调实例:Tom做题 数学老师让Tom做一道题,并且Tom做题期间数学老师不用盯着Tom,而是在玩手机,等Tom把题目做完后再把答案告诉老师。 1 数学老师需要Tom的一个引用,然后才能将题目发给Tom。 2 数学老师需要提供一个方法以便Tom做完题目以后能够将答案告诉他。 3 Tom需要数学老师的一个引用,以便Tom把答案给这位老师,而不是隔壁的体育老师。 回调接口,可以理解为老师接口 //回调指的是A调用B来做一件事,B做完以后将结果告诉给A,这期间A可以做别的事情。 //这个接口中有一个方法,意为B做完题目后告诉A时使用的方法。 //所以我们必须提供这个接口以便让B来回调。 //回调接口, public interface CallBack { void tellAnswer(int res); } 数学老师类 //老师类实例化回调接口,即学生写完题目之后通过老师的提供的方法进行回调。 //那么学生如何调用到老师的方法呢,只要在学生类的方法中传入老师的引用即可。 //而老师需要指定学生答题,所以也要传入学生的实例。 public class Teacher implements CallBack{ private Student student; Teacher(Student student) { this.student = student; } void askProblem (Student student, Teacher teacher) { //main方法是主线程运行,为了实现异步回调,这里开启一个线程来操作 new Thread(new Runnable() { @Override public void run() { student.resolveProblem(teacher); } }).start(); //老师让学生做题以后,等待学生回答的这段时间,可以做别的事,比如玩手机.\ //而不需要同步等待,这就是回调的好处。 //当然你可以说开启一个线程让学生做题就行了,但是这样无法让学生通知老师。 //需要另外的机制去实现通知过程。 // 当然,多线程中的future和callable也可以实现数据获取的功能。 for (int i = 1;i < 4;i ++) { System.out.println("等学生回答问题的时候老师玩了 " + i + "秒的手机"); } } @Override public void tellAnswer(int res) { System.out.println("the answer is " + res); } } 学生接口 //学生的接口,解决问题的方法中要传入老师的引用,否则无法完成对具体实例的回调。 //写为接口的好处就是,很多个学生都可以实现这个接口,并且老师在提问题时可以通过 //传入List<Student>来聚合学生,十分方便。 public interface Student { void resolveProblem (Teacher teacher); } 学生Tom public class Tom implements Student{ @Override public void resolveProblem(Teacher teacher) { try { //学生思考了3秒后得到了答案,通过老师提供的回调方法告诉老师。 Thread.sleep(3000); System.out.println("work out"); teacher.tellAnswer(111); } catch (InterruptedException e) { e.printStackTrace(); } } 测试类 public class Test { public static void main(String[] args) { //测试 Student tom = new Tom(); Teacher lee = new Teacher(tom); lee.askProblem(tom, lee); //结果 // 等学生回答问题的时候老师玩了 1秒的手机 // 等学生回答问题的时候老师玩了 2秒的手机 // 等学生回答问题的时候老师玩了 3秒的手机 // work out // the answer is 111 } } 多线程中的“回调” Java多线程中可以通过callable和future或futuretask结合来获取线程执行后的返回值。实现方法是通过get方法来调用callable的call方法获取返回值。 其实这种方法本质上不是回调,回调要求的是任务完成以后被调用者主动回调调用者的接口。而这里是调用者主动使用get方法阻塞获取返回值。 public class 多线程中的回调 { //这里简单地使用future和callable实现了线程执行完后 public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newCachedThreadPool(); Future<String> future = executor.submit(new Callable<String>() { @Override public String call() throws Exception { System.out.println("call"); TimeUnit.SECONDS.sleep(1); return "str"; } }); //手动阻塞调用get通过call方法获得返回值。 System.out.println(future.get()); //需要手动关闭,不然线程池的线程会继续执行。 executor.shutdown(); //使用futuretask同时作为线程执行单元和数据请求单元。 FutureTask<Integer> futureTask = new FutureTask(new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("dasds"); return new Random().nextInt(); } }); new Thread(futureTask).start(); //阻塞获取返回值 System.out.println(futureTask.get()); } @Test public void test () { Callable callable = new Callable() { @Override public Object call() throws Exception { return null; } }; FutureTask futureTask = new FutureTask(callable); } }

优秀的个人博客,低调大师

Java基础8:深入理解内部类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a724888/article/details/80087616 这位大侠,这是我的公众号:程序员江湖。 分享程序员面试与技术的那些事。 干货满满,关注就送。 本文主要介绍了Java内部类的基本原理,使用方法和各种细节。 有关内部类实现回调,事件驱动和委托机制的文章将在后面发布。 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl.github.io/2018/04/25/javase8 内部类初探 一、什么是内部类? 内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类 二、内部类的共性 (1)内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。 (2)内部类不能用普通的方式访问。 (3)内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量 。 (4)外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问 内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。 因为当某个外围类的对象创建内部类的对象时,此内部类会捕获一个隐式引用,它引用了实例化该内部对象的外围类对象。通过这个指针,可以访问外围类对象的全部状态。 通过反编译内部类的字节码,分析之后主要是通过以下几步做到的: 1 编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象的引用; 2 编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为1中添加的成员变量赋值; 3 在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。 二、使用内部类的好处: 静态内部类的作用: 1 只是为了降低包的深度,方便类的使用,静态内部类适用于包含类当中,但又不依赖与外在的类。 2 由于Java规定静态内部类不能用使用外在类的非静态属性和方法,所以只是为了方便管理类结构而定义。于是我们在创建静态内部类的时候,不需要外部类对象的引用。 非静态内部类的作用: 1 内部类继承自某个类或实现某个接口,内部类的代码操作创建其他外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。 2 使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响 3 如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。 从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了”多重继承”。 三、 那静态内部类与普通内部类有什么区别呢?问得好,区别如下: (1)静态内部类不持有外部类的引用 在普通内部类中,我们可以直接访问外部类的属性、方法,即使是private类型也可以访问,这是因为内部类持有一个外部类的引用,可以自由访问。而静态内部类,则只可以访问外部类的静态方法和静态属性(如果是private权限也能访问,这是由其代码位置所决定的),其他则不能访问。 (2)静态内部类不依赖外部类 普通内部类与外部类之间是相互依赖的关系,内部类实例不能脱离外部类实例,也就是说它们会同生同死,一起声明,一起被垃圾回收器回收。而静态内部类是可以独立存在的,即使外部类消亡了,静态内部类还是可以存在的。 (3)普通内部类不能声明static的方法和变量 普通内部类不能声明static的方法和变量,注意这里说的是变量,常量(也就是final static修饰的属性)还是可以的,而静态内部类形似外部类,没有任何限制。 ==为什么普通内部类不能有静态变量呢?== 1 成员内部类 之所以叫做成员 就是说他是类实例的一部分 而不是类的一部分 2 结构上来说 他和你声明的成员变量是一样的地位 一个特殊的成员变量 而静态的变量是类的一部分和实例无关 3 你若声明一个成员内部类 让他成为主类的实例一部分 然后又想在内部类声明和实例无关的静态的东西 你让JVM情何以堪啊 4 若想在内部类内声明静态字段 就必须将其内部类本身声明为静态 非静态内部类有一个很大的优点:可以自由使用外部类的所有变量和方法 下面的例子大概地介绍了 1 非静态内部类和静态内部类的区别。 2 不同访问权限的内部类的使用。 3 外部类和它的内部类之间的关系 //本节讨论内部类以及不同访问权限的控制 //内部类只有在使用时才会被加载。 //外部类B public class B{ int i = 1; int j = 1; static int s = 1; static int ss = 1; A a; AA aa; AAA aaa; //内部类A public class A { // static void go () { // // } // static { // // } // static int b = 1;//非静态内部类不能有静态成员变量和静态代码块和静态方法, // 因为内部类在外部类加载时并不会被加载和初始化。 //所以不会进行静态代码的调用 int i = 2;//外部类无法读取内部类的成员,而内部类可以直接访问外部类成员 public void test() { System.out.println(j); j = 2; System.out.println(j); System.out.println(s);//可以访问类的静态成员变量 } public void test2() { AA aa = new AA(); AAA aaa = new AAA(); } } //静态内部类S,可以被外部访问 public static class S { int i = 1;//访问不到非静态变量。 static int s = 0;//可以有静态变量 public static void main(String[] args) { System.out.println(s); } @Test public void test () { // System.out.println(j);//报错,静态内部类不能读取外部类的非静态变量 System.out.println(s); System.out.println(ss); s = 2; ss = 2; System.out.println(s); System.out.println(ss); } } //内部类AA,其实这里加protected相当于default //因为外部类要调用内部类只能通过B。并且无法直接继承AA,所以必须在同包 //的类中才能调用到(这里不考虑静态内部类),那么就和default一样了。 protected class AA{ int i = 2;//内部类之间不共享变量 public void test (){ A a = new A(); AAA aaa = new AAA(); //内部类之间可以互相访问。 } } //包外部依然无法访问,因为包没有继承关系,所以找不到这个类 protected static class SS{ int i = 2;//内部类之间不共享变量 public void test (){ //内部类之间可以互相访问。 } } //私有内部类A,对外不可见,但对内部类和父类可见 private class AAA { int i = 2;//内部类之间不共享变量 public void test() { A a = new A(); AA aa = new AA(); //内部类之间可以互相访问。 } } @Test public void test(){ A a = new A(); a.test(); //内部类可以修改外部类的成员变量 //打印出 1 2 B b = new B(); } } //另一个外部类 class C { @Test public void test() { //首先,其他类内部类只能通过外部类来获取其实例。 B.S s = new B.S(); //静态内部类可以直接通过B类直接获取,不需要B的实例,和静态成员变量类似。 //B.A a = new B.A(); //当A不是静态类时这行代码会报错。 //需要使用B的实例来获取A的实例 B b = new B(); B.A a = b.new A(); B.AA aa = b.new AA();//B和C同包,所以可以访问到AA // B.AAA aaa = b.new AAA();AAA为私有内部类,外部类不可见 //当A使用private修饰时,使用B的实例也无法获取A的实例,这一点和私有变量是一样的。 //所有普通的内部类与类中的一个变量是类似的。静态内部类则与静态成员类似。 } } 内部类的加载 可能刚才的例子中没办法直观地看到内部类是如何加载的,接下来用例子展示一下内部类加载的过程。 1 内部类是延时加载的,也就是说只会在第一次使用时加载。不使用就不加载,所以可以很好的实现单例模式。 2 不论是静态内部类还是非静态内部类都是在第一次使用时才会被加载。 3 对于非静态内部类是不能出现静态模块(包含静态块,静态属性,静态方法等) 4 非静态类的使用需要依赖于外部类的对象,详见上述对象innerClass 的初始化。 简单来说,类的加载都是发生在类要被用到的时候。内部类也是一样 1 普通内部类在第一次用到时加载,并且每次实例化时都会执行内部成员变量的初始化,以及代码块和构造方法。 2 静态内部类也是在第一次用到时被加载。但是当它加载完以后就会将静态成员变量初始化,运行静态代码块,并且只执行一次。当然,非静态成员和代码块每次实例化时也会执行。 总结一下Java类代码加载的顺序,万变不离其宗。 规律一、初始化构造时,先父后子;只有在父类所有都构造完后子类才被初始化 规律二、类加载先是静态、后非静态、最后是构造函数。 静态构造块、静态类属性按出现在类定义里面的先后顺序初始化,同理非静态的也是一样的,只是静态的只在加载字节码时执行一次,不管你new多少次,非静态会在new多少次就执行多少次 规律三、java中的类只有在被用到的时候才会被加载 规律四、java类只有在类字节码被加载后才可以被构造成对象实例 成员内部类 在方法中定义的内部类称为局部内部类。与局部变量类似,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,和此外围类所有的成员。 需要注意的是: 局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。 public class 局部内部类 { class A {//局部内部类就是写在方法里的类,只在方法执行时加载,一次性使用。 public void test() { class B { public void test () { class C { } } } } } @Test public void test () { int i = 1; final int j = 2; class A { @Test public void test () { System.out.println(i); System.out.println(j); } } A a = new A(); System.out.println(a); } static class B { public static void test () { //static class A报错,方法里不能定义静态内部类。 //因为只有在方法调用时才能进行类加载和初始化。 } } } 匿名内部类 简单地说:匿名内部类就是没有名字的内部类,并且,匿名内部类是局部内部类的一种特殊形式。什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的: 只用到类的一个实例。 类在定义后马上用到。 类非常小(SUN推荐是在4行代码以下) 给类命名并不会导致你的代码更容易被理解。 在使用匿名内部类时,要记住以下几个原则: 1 匿名内部类不能有构造方法。 2 匿名内部类不能定义任何静态成员、方法和类。 3 匿名内部类不能是public,protected,private,static。 4 只能创建匿名内部类的一个实例。 5 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。 6 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。 一个匿名内部类的例子: public class 匿名内部类 { } interface D{ void run (); } abstract class E{ E (){ } abstract void work(); } class A { @Test public void test (int k) { //利用接口写出一个实现该接口的类的实例。 //有且仅有一个实例,这个类无法重用。 new Runnable() { @Override public void run() { // k = 1;报错,当外部方法中的局部变量在内部类使用中必须改为final类型。 //因为方外部法中即使改变了这个变量也不会反映到内部类中。 //所以对于内部类来讲这只是一个常量。 System.out.println(100); System.out.println(k); } }; new D(){ //实现接口的匿名类 int i =1; @Override public void run() { System.out.println("run"); System.out.println(i); System.out.println(k); } }.run(); new E(){ //继承抽象类的匿名类 int i = 1; void run (int j) { j = 1; } @Override void work() { } }; } } 匿名内部类里的final 使用的形参为何要为final 参考文件:http://android.blog.51cto.com/268543/384844 我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。 为什么必须要为final呢? 首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用: public class OuterClass { public void display(final String name,String age){ class InnerClass{ void display(){ System.out.println(name); } } } } 从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下: public class OuterClass$InnerClass { public InnerClass(String name,String age){ this.InnerClass$name = name; this.InnerClass$age = age; } public void display(){ System.out.println(this.InnerClass$name + "----" + this.InnerClass$age ); } } 所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。 直到这里还没有解释为什么是final 在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的。 毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。 简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。 故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。 内部类初始化 我们一般都是利用构造器来完成某个实例的初始化工作的,但是匿名内部类是没有构造器的!那怎么来初始化匿名内部类呢?使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果。 public class OutClass { public InnerClass getInnerClass(final int age,final String name){ return new InnerClass() { int age_ ; String name_; //构造代码块完成初始化工作 { if(0 < age && age < 200){ age_ = age; name_ = name; } } public String getName() { return name_; } public int getAge() { return age_; } }; } 内部类的重载 如果你创建了一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?也就是说,内部类可以被重载吗?这看起来似乎是个很有用的点子,但是“重载”内部类就好像它是外围类的一个方法,其实并不起什么作用: class Egg { private Yolk y; protected class Yolk { public Yolk() { System.out.println("Egg.Yolk()"); } } public Egg() { System.out.println("New Egg()"); y = new Yolk(); } } public class BigEgg extends Egg { public class Yolk { public Yolk() { System.out.println("BigEgg.Yolk()"); } } public static void main(String[] args) { new BigEgg(); } } 复制代码 输出结果为: New Egg() Egg.Yolk() 缺省的构造器是编译器自动生成的,这里是调用基类的缺省构造器。你可能认为既然创建了BigEgg 的对象,那么所使用的应该是被“重载”过的Yolk,但你可以从输出中看到实际情况并不是这样的。 这个例子说明,当你继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的两个实体,各自在自己的命名空间内。 内部类的继承 因为内部类的构造器要用到其外围类对象的引用,所以在你继承一个内部类的时候,事情变得有点复杂。问题在于,那个“秘密的”外围类对象的引用必须被初始化,而在被继承的类中并不存在要联接的缺省对象。要解决这个问题,需使用专门的语法来明确说清它们之间的关联: class WithInner { class Inner { Inner(){ System.out.println("this is a constructor in WithInner.Inner"); }; } } public class InheritInner extends WithInner.Inner { // ! InheritInner() {} // Won't compile InheritInner(WithInner wi) { wi.super(); System.out.println("this is a constructor in InheritInner"); } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } } 复制代码 输出结果为: this is a constructor in WithInner.Inner this is a constructor in InheritInner 可以看到,InheritInner 只继承自内部类,而不是外围类。但是当要生成一个构造器时,缺省的构造器并不算好,而且你不能只是传递一个指向外围类对象的引用。此外,你必须在构造器内使用如下语法: enclosingClassReference.super(); 这样才提供了必要的引用,然后程序才能编译通过。 有关匿名内部类实现回调,事件驱动,委托等机制的文章将在下一节讲述。

优秀的个人博客,低调大师

Cookie与Session基础知识点

会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份Session通过在服务器端记录信息确定用户身份 本章将系统地讲述Cookie与Session机制,并比较说明什么时候不能用Cookie,什么时候不能用Session。 1.1 Cookie机制 在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。 而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。 Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。 1.1.1 什么是Cookie 由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。 Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。 查看某个网站颁发的Cookie很简单。在浏览器地址栏输入javascript:alert (document. cookie)就可以了(需要有网才能查看)。JavaScript脚本会弹出一个对话框显示本网站颁发的所有Cookie的内容 其中第一行BAIDUID记录的就是笔者的身份,只是Baidu使用特殊的方法将Cookie信息加密了。 注意:Cookie功能需要浏览器的支持。 如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。 不同的浏览器采用不同的方式保存Cookie。 IE浏览器会在“C:\Documents and Settings\你的用户名\Cookies”文件夹下以文本文件形式保存,一个文本文件保存一个Cookie。 1.1.2 记录用户访问次数 Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器通过操作Cookie类对象对客户端Cookie进行操作。通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组形式返回),通过response.addCookie(Cookiecookie)向客户端设置Cookie。 Cookie对象使用key-value属性对的形式保存用户状态,一个Cookie对象保存一个属性对,一个request或者response同时使用多个Cookie。因为Cookie类位于包javax.servlet.http.*下面,所以JSP中不需要import该类。 1.1.3 Cookie的不可跨域名性 很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢? 答案是否定的。Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。 Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。 需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。 注意:用户登录网站www.google.com之后会发现访问images.google.com时登录信息仍然有效,而普通的Cookie是做不到的。这是因为Google做了特殊处理。本章后面也会对Cookie做类似的处理。 1.1.4 Unicode编码:保存中文 中文与英文字符不同,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。 提示:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。 1.1.5 BASE64编码:保存二进制图片 Cookie不仅可以使用ASCII字符与Unicode字符,还可以使用二进制数据。例如在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。 注意:由于浏览器每次请求服务器都会携带Cookie,因此Cookie内容不宜过多,否则影响速度。Cookie的内容应该少而精。 1.1.6 设置Cookie的所有属性 除了name与value之外,Cookie还具有其他几个常用的属性。每个属性对应一个getter方法与一个setter方法。Cookie类的所有属性如表1.1所示。 image.png 1.1.7 Cookie的有效期 Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。 如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。 Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie cookie.setMaxAge(Integer.MAX_VALUE); // 设置生命周期为MAX_VALUE response.addCookie(cookie); // 输出到客户端 如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。 maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。 如果maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除, 例如: Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie cookie.setMaxAge(0); // 设置生命周期为0,不能为负数 response.addCookie(cookie); // 必须执行这一句 response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。 要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。 注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。 1.1.8 Cookie的修改、删除 Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。 如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证,设置不同的属性。 注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。 1.1.9 Cookie的域名 Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。 正常情况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数,例如: Cookie cookie = new Cookie("time","20080808"); // 新建Cookie cookie.setDomain(".helloweenvsfei.com"); // 设置域名 cookie.setPath("/"); // 设置路径 cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期 response.addCookie(cookie); // 输出到客户端 读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。 注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。 1.1.10 Cookie的路径 domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。 例如,如果只允许/session/下的程序使用Cookie,可以这么写: Cookie cookie = new Cookie("time","20080808"); // 新建Cookie cookie.setPath("/session/"); // 设置路径 response.addCookie(cookie); // 输出到客户端 设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。name相同但domain同同的两个Cookie也是不同的 注意:页面只能获取它属于的Path的Cookie。例如/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。使用时一定要注意。 1.1.11 Cookie的安全属性 HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输很机密的内容是一种隐患。 如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。下面的代码设置secure属性为true: Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie cookie.setSecure(true); // 设置安全属性 response.addCookie(cookie); // 输出到客户端 提示:secure属性并不能对Cookie内容加密,不能保证绝对的安全性。 如果需要高安全性,**需要在程序中对Cookie内容加密 1.1.12 JavaScript操作Cookie Cookie是保存在浏览器端的,因此浏览器具有操作Cookie的先决条件。 浏览器可以使用脚本程序如JS或者VBScript等操作Cookie。 这里以JavaScript为例介绍常用的Cookie操作。 例如下面的代码会输出本页面所有的Cookie。<script>document.write(document.cookie);</script> 由于JavaScript能够任意地读写Cookie,有些好事者便想使用JavaScript程序去窥探用户在其他网站的Cookie。不过这是徒劳的,W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了,W3C标准的浏览器会阻止JavaScript读写任何不属于自己网站的Cookie。换句话说,A网站的JavaScript程序读写B网站的Cookie不会有任何结果。 1.1.13 案例:永久登录 如果用户是在自己家的电脑上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。 实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。 保存登录信息有多种方案 最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中。 还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。 这几种方案验证账号时都要查询数据库。 本例将采用另一种方案,只在登录时查询一次数据库,以后访问验证登录信息时不再查询数据库。实现方式是把账号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可。 本例把账号保存到名为account的Cookie中,把账号连同密钥用MD5算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。 登录时可以选择登录信息的有效期:关闭浏览器即失效、30天内有效与永久有效。通过设置Cookie的age属性来实现,注意观察代码 提示:该加密机制中最重要的部分为算法与密钥。由于MD5算法的不可逆性,即使用户知道了账号与加密后的字符串,也不可能解密得到密钥。因此,只要保管好密钥与算法,该机制就是安全的。 1.2 Session机制 Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。 1.2.1 什么是Session Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。 客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。 如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了 1.2.2 实现用户登录 Session对应的类为javax.servlet.http.HttpSession类。 每个来访者对应一个Session对象,所有该客户的状态信息都保存在这个Session对象里。Session对象是在客户端第一次请求服务器的时候创建的 Session也是一种key-value的属性对,通过getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法读写客户状态信息 Servlet里通过request.getSession()方法获取该客户的Session request还可以使用getSession(boolean create)来获取Session。区别是如果该客户的Session不存在,request.getSession()方法会返回null,而getSession(true)会先创建Session再将Session返回。 Servlet中必须使用request来编程式获取HttpSession对象,而JSP中内置了Session隐藏对象,可以直接使用。如果使用声明了<%@page session="false" %>,则Session隐藏对象不可用。 当多个客户端执行程序时,服务器会保存多个客户端的Session。获取Session的时候也不需要声明获取谁的Session。Session机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见。 提示:Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。 1.2.3 Session的生命周期 Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简 Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。 Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。 1.2.4 Session的有效期 由于会有越来越多的用户访问服务器,因此Session也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。 Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。 Session的超时时间也可以在web.xml中修改。另外,通过调用Session的invalidate()方法可以使Session失效。 1.2.5 Session的常用方法 Session中包括各种方法,使用起来要比Cookie方便得多。Session的常用方法如表1.2所示。 image.png Tomcat中Session的默认超时时间为20分钟。通过setMaxInactiveInterval(int seconds)修改超时时间。可以修改web.xml改变Session的默认超时时间。例如修改为60分钟: <session-config> <session-timeout>60</session-timeout> </session-config> 注意:<session-timeout>参数的单位为分钟,而setMaxInactiveInterval(int s)单位为秒。1.2.6 Session对浏览器的要求 虽然Session保存在服务器,对客户端是透明的,它的正常运行仍然需要客户端浏览器的支持。这是因为Session需要使用Cookie作为识别标志。HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。 该Cookie为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。 因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览器图标等打开的窗口)除外。这类子窗口会共享父窗口的Cookie,因此会共享一个Session。 注意:新开的浏览器窗口会生成新的Session,但子窗口除外。子窗口会共用父窗口的Session。例如,在链接上右击,在弹出的快捷菜单中选择“在新窗口中打开”时,子窗口便可以访问父窗口的Session。 如果客户端浏览器将Cookie功能禁用,或者不支持Cookie怎么办?例如,绝大多数的手机浏览器都不支持Cookie。Java Web提供了另一种解决方案:URL地址重写。 1.2.7 URL地址重写 URL地址重写是对客户端不支持Cookie的解决方案。 URL地址重写的原理是将该用户Session的id信息重写到URL地址中。 服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写,例如: <td> <a href="<%=response.encodeURL("index.jsp?c=1&wd=Java") %>"> Homepage</a> </td> 该方法会自动判断客户端是否支持Cookie。如果客户端支持Cookie,会将URL原封不动地输出来。如果客户端不支持Cookie,则会将用户Session的id重写到URL中。重写后的输出可能是这样的: <td> <ahref="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c= 1&wd=Java">Homepage</a> </td> 即在文件名的后面,在URL参数的前面添加了字符串“;jsessionid=XXX”。其中XXX为Session的id。 分析一下可以知道,增添的jsessionid字符串既不会影响请求的文件名,也不会影响提交的地址栏参数。用户单击这个链接的时候会把Session的id通过URL提交到服务器上,服务器通过解析URL地址获得Session的id。 如果是页面重定向(Redirection),URL地址重写可以这样写: <% if(“administrator”.equals(userName)) { response.sendRedirect(response.encodeRedirectURL(“administrator.jsp”)); return; } %> 效果跟response.encodeURL(String url)是一样的:如果客户端支持Cookie,生成原URL地址,如果不支持Cookie,传回重写后的带有jsessionid字符串的地址。 对于WAP程序,由于大部分的手机浏览器都不支持Cookie,WAP程序都会采用URL地址重写来跟踪用户会话。比如用友集团的移动商街等。 注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,但是由于第一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次访问时服务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。 1.2.8 Session中禁止使用Cookie Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie。 打开项目sessionWeb的WebRoot目录下的META-INF文件夹(跟WEB-INF文件夹同级,如果没有则创建),打开context.xml(如果没有则创建),编辑内容如下: 代码1.11 /META-INF/context.xml <?xml version='1.0' encoding='UTF-8'?> <Context path="/sessionWeb"cookies="false"> </Context> 或者修改Tomcat全局的conf/context.xml,修改内容如下: 代码1.12 context.xml <!-- The contents of this file will be loaded for eachweb application --> <Context cookies="false"> <!-- ... 中间代码略 --> </Context> 部署后TOMCAT便不会自动生成名JSESSIONID的Cookie,Session也不会以Cookie为识别标志,而仅仅以重写后的URL地址为识别标志了。 注意:该配置只是禁止Session使用Cookie作为识别标志,并不能阻止其他的Cookie读写。也就是说服务器不会自动维护名为JSESSIONID的Cookie了,但是程序中仍然可以读写其他的Cookie。

优秀的个人博客,低调大师

02 - JavaSE之基础及面向对象(补充)

引用数据类型 java中的数据类型可以分为 基本数据类型 和 引用数据类型 两大类 int float char boolean等都是基本数据类型 类类型都是引用数据类型 引用数据类型类似C语言中的指针,多个引用数据类型可以引用同一个对象 基本数据类型的变量都有一份自己的值拷贝,而引用数据类型都是指向自己堆空间的值。 多态 java中的多态只能发生于方法,子父类间完全相同的属性不会发生多态。 子父类间完全相同的属性通过引用的类型而不是实际对象的类型确定访问哪一个。 public static void main(String[] args) 分析: public声明main是公共的,这样JVM才可以访问main方法,这个时候相当于跨包调用,所以修饰为public,包含这个main方法的类名也要修饰为public也是同样的原因,因为JVM需要找得到它和它中的main方法。 static表示静态的,表示main方法不需要new一个对象就可以访问。 void main(String[] args)是方法返回值,方法名和方法参数。 java语言中可以在子类重写的方法前加上 **@Override** 提示字,这样编译器会检查方法重写的是否合法。 这样真好,避免重写时函数名等写错,然而编译器又不会报错的尴尬局面。 一个子类可以实现多个接口,但是必须实现接口中的所有方法,否则子类将变成抽象类。

优秀的个人博客,低调大师

android霓虹灯源代码——基础

android霓虹灯 霓:有时在虹的外侧还能看到第二道虹,光彩比第一道虹稍淡,色序是外紫内红,与虹相反。 虹:原意也是一种自然现象,就是彩虹,也是七彩的,色序从外至内分别为:赤、橙、黄、绿、蓝、靛、紫。 霓虹灯:夜间用来吸引顾客,或装饰夜景的彩色灯,所以用“霓虹”这两种美丽的东西来作为这种灯的名字。 让我们看一下源代码: packagecom.smart.activiy; importandroid.app.Activity; importandroid.os.Bundle; importandroid.os.Handler; importandroid.view.View; publicclassMainextendsActivityimplementsRunnable{ //5个TextView的颜色值 privateint[]colors=newint[] {0xFFFF0000,0xFF00FF00,0xFF0000FF,0xFFFF00FF,0xFF00FFFF}; //每一次颜色的下一个颜色的索引,最后一个颜色的下一个颜色是第一个颜色,相当于循环链表 privateint[]nextColorPointers=newint[] {1,2,3,4,0}; privateView[]views;//保存5个TextView privateintcurrentColorPointer=0;//当前颜色索引(指针) privateHandlerhandler; @Override publicvoidrun() { intnextColorPointer=currentColorPointer; for(inti=views.length-1;i>=0;i--) { //设置当前TextView的背景颜色 views[i] .setBackgroundColor(colors[nextColorPointers[nextColorPointer]]); //获得下一个TextView的背景颜色值的索引(指针) nextColorPointer=nextColorPointers[nextColorPointer]; } currentColorPointer++; if(currentColorPointer==5) currentColorPointer=0; handler.postDelayed(this,300);//第300毫秒循环一次 } @Override publicvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //初始化views数组 views=newView[] {findViewById(R.id.textview5),findViewById(R.id.textview4), findViewById(R.id.textview3),findViewById(R.id.textview2), findViewById(R.id.textview1)}; handler=newHandler(); handler.postDelayed(this,300);//第300毫秒循环一次 } } main.xml <?xmlversion="1.0"encoding="utf-8"?> <FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"android:layout_height="fill_parent"> <TextViewandroid:id="@+id/textview1"android:layout_width="300dp" android:layout_height="300dp"android:layout_gravity="center"/> <TextViewandroid:id="@+id/textview2"android:layout_width="240dp" android:layout_height="240dp"android:layout_gravity="center"/> <TextViewandroid:id="@+id/textview3"android:layout_width="180dp" android:layout_height="180dp"android:layout_gravity="center"/> <TextViewandroid:id="@+id/textview4"android:layout_width="120dp" android:layout_height="120dp"android:layout_gravity="center"/> <TextViewandroid:id="@+id/textview5"android:layout_width="60dp" android:layout_height="60dp"android:layout_gravity="center"/> </FrameLayout> 本文转自 llb988 51CTO博客,原文链接:http://blog.51cto.com/llb988/497372,如需转载请自行联系原作者

优秀的个人博客,低调大师

android计算器布局界面——基础

计算器布局,是初学者学习的过程,下面让我们看看它的界面是如何的。 main.xml代码 <?xmlversion="1.0"encoding="utf-8"?> <TableLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TableRow> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="7"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="8"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="9"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="/"/> </TableRow> <TableRow> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="4"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="5"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="6"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="*"/> </TableRow> <TableRow> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="2"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="3"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="."/> </TableRow> <TableRow> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="="/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="-"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+"/> </TableRow> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </TableLayout> 本文转自 llb988 51CTO博客,原文链接:http://blog.51cto.com/llb988/491627,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android基础---获取手机硬件数据(转)

1、IMEI(International Mobile Equipment Identity) 是国际移动设备身份码的缩写, 国际移动装备辨识码,是由15位数字组成的"电子串号",它与每台手机一一对应,而且该码是全世界唯一的。 每一只手机在组装完成后都将被赋予一个全球唯一的一组号码,这个号码从生产到交付使用都将被制造生产的厂商所记录。 2、IMSI 国际移动用户识别码(IMSI:International Mobile SubscriberIdentification Number) 是区别移动用户的标志,储存在SIM卡中,可用于区别移动用户的有效信息。其总长度不超过15位,同样使用0~9的数字。 IMSI共有15位,其结构如下: MCC+MNC+MSIN MCC:Mobile Country Code,移动国家码,MCC的资源由国际电联(ITU)统一分配和管理,唯一识别移动用户所属的国家,共3位,中国为460; MNC:Mobile Network Code,移动网络码,共2位,中国移动系统使用00、02、07,中国联通GSM系统使用01,中国电信CDMA系统使用03,一个典型的IMSI号码为460030912121001; MSIN:Mobile Subscriber Identification Number共有10位,其结构如下: EF+M0M1M2M3+ABCD 其中的M0M1M2M3和MDN号码中的H0H1H2H3可存在对应关系,ABCD四位为自由分配。 可以看出IMSI在NMSI号码前加了MCC,可以区别出每个用户的来自的国家,因此可以实现国际漫游。在同一个国家内,如果有多个移动网络运营商,可以通过MNC来进行区别. 需要权限: <uses-permission android:name= "android.permission.READ_PHONE_STATE" /> public class DeviceInfo { public static void getDeviceInfo(Context context) { TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); String number = tm.getLine1Number(); //本机号码(多半不可取) String imei = tm.getDeviceId(); //(设备序列号全球唯一, IMEI) String businessName = tm.getNetworkOperatorName(); //运营商名字 String sim = tm.getSimSerialNumber(); //SIM卡序列号 String imsi = tm.getSubscriberId(); //IMSI String iso = tm.getNetworkCountryIso(); //设备所属国家 cn或者us 之类 String businessId = tm.getNetworkOperator(); //运营商ID System.out.println( " number : " +number + " imei " +imei + " bussnessName " +businessName + " sim " +sim + " imsi " +imsi + " iso " +iso + " businessId " +businessId ); } } 本文转自demoblog博客园博客,原文链接http://www.cnblogs.com/0616--ataozhijia/archive/2012/11/29/2795272.html如需转载请自行联系原作者 demoblog

优秀的个人博客,低调大师

Android学习笔记(三)基础知识(2)

交互对话框 使用Android SDK中,具有交互功能的对话框是AlertDialog窗口。 package com.example.test8; import android.os.Bundle; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.support.v4.app.NavUtils; public class MainActivity extends Activity { private Button bt; int n=0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt=(Button)findViewById(R.id.button1); bt.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub n++; String str=Integer.toString(n); bt.setText("点击次数:"+n); new AlertDialog.Builder(MainActivity.this) .setTitle("提示") .setMessage("对话框") .setPositiveButton("确定",new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub } } ) .show(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } } 拖放相片特效 在Android中,拖动图片的特效可以通过Android.content.Context,Android,widget.BaseAdapter和Android.widget.imageView等来实现。 在下面的列子中,context可以像画布一样,随时可以被处理覆盖。Context是作为Android.content子类。通过widget.BaseAdapter作为容器来存放Gallery所需要的图片。 这里是通过使用Gallery实现的相片拖拽功能,具体代码如下: package com.example.test9; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Gallery; import android.widget.ImageView; import android.widget.TextView; import android.support.v4.app.NavUtils; public class MainActivity extends Activity { private TextView textview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textview = (TextView)findViewById(R.id.textView1); textview.setText("特效演示"); textview.setTextColor(Color.BLUE); Gallery gallery=(Gallery)findViewById(R.id.gallery1); gallery.setAdapter(new ImageAdapter(this)); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public class ImageAdapter extends BaseAdapter{ private Context myContext; private int[] myImageID={android.R.drawable.btn_minus, android.R.drawable.btn_radio, android.R.drawable.ic_lock_idle_low_battery, android.R.drawable.ic_menu_camera }; public ImageAdapter(Context c){ this.myContext=c; } public int getCount() { // TODO Auto-generated method stub return this.myImageID.length; } public Object getItem(int position) { // TODO Auto-generated method stub return position; } public long getItemId(int position) { // TODO Auto-generated method stub return position; } public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ImageView i=new ImageView(this.myContext); i.setImageResource(this.myImageID[position]); i.setScaleType(ImageView.ScaleType.FIT_XY); i.setLayoutParams(new Gallery.LayoutParams(120,120)); return i; } } } 设置about关于信息 创建一个onCreateOptionsMenu(Menu menu)类函数,用于添加Menu菜单项,再利用onOptionItemSelected选择菜单项 package com.example.test10; import android.os.Bundle; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.view.Menu; import android.view.MenuItem; import android.support.v4.app.NavUtils; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0,0,0,"关于"); menu.add(0,1,1,"退出"); return super.onCreateOptionsMenu(menu); } public boolean onOptionsItemSelected(MenuItem item){ super.onOptionsItemSelected(item); switch(item.getItemId()){ case 0: openOptionsDialog(); break; case 1: finish(); } return true; } private void openOptionsDialog(){ new AlertDialog.Builder(this) .setTitle("关于") .setMessage("***") .setPositiveButton("OK",new OnClickListener() { public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub } }) .show(); } } 程序加载中状态框 程序加载中的提示,此功能是通过Progress Dialog类来运行,此类在Android.app.ProgressDialog类里。但是,我们需要注意使用dismiss()函数来关闭取得焦点的对话框,否则程序将陷入死循环,也不能在线程里更改CONTEXT或parent view的任何状态。 package com.example.test11; import android.os.Bundle; import android.app.Activity; import android.app.ProgressDialog; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.support.v4.app.NavUtils; public class MainActivity extends Activity { private TextView text; private Button bt; public ProgressDialog myDialog=null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text=(TextView)findViewById(R.id.textView1); bt=(Button)findViewById(R.id.button1); bt.setOnClickListener(myShowProgressBar); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } Button.OnClickListener myShowProgressBar=new Button.OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub final CharSequence strDialogTile="请等待"; final CharSequence strDialogBody="程序正在运行"; myDialog=ProgressDialog.show(MainActivity.this,strDialogTile,strDialogBody,true); text.setText(strDialogBody); new Thread(){ public void run(){ try{ text.setText("start"); sleep(3000); text.setText("end"); }catch (Exception e) { // TODO: handle exception e.printStackTrace(); } finally{ myDialog.dismiss(); } } }.start(); } }; } 本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/archive/2013/05/16/3076677.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Hbase Shell 基础和常用命令

HBase是一个分布式的、面向列的开源数据库,源于google的一篇论文《bigtable:一个结构化数据的分布式存储系统》。HBase是Google Bigtable的开源实现,它利用hadoopHDFS作为其文件存储系统,利用Hadoop MapReduce来处理HBase中的海量数据,利用Zookeeper作为协同服务。 1. 简介 HBase是一个分布式的、面向列的开源数据库,源于google的一篇论文《bigtable:一个结构化数据的分布式存储系统》。HBase是Google Bigtable的开源实现,它利用Hadoop HDFS作为其文件存储系统,利用Hadoop MapReduce来处理HBase中的海量数据,利用Zookeeper作为协同服务。 2. HBase的表结构 HBase以表的形式存储数据。表有行和列组成。列划分为若干个列族/列簇(column family)。 Row Key column-family1 column-family2 column-family3 column1 column2 column1 column2 column3 column1 key1 key2 key3 如上图所示,key1,key2,key3是三条记录的唯一的row key值,column-family1,column-family2,column-family3是三个列族,每个列族下又包括几列。比如column-family1这个列族下包括两列,名字是column1和column2,t1:abc,t2:gdxdf是由row key1和column-family1-column1唯一确定的一个单元cell。这个cell中有两个数据,abc和gdxdf。两个值的时间戳不一样,分别是t1,t2, hbase会返回最新时间的值给请求者。 这些名词的具体含义如下: (1) Row Key 与nosql数据库们一样,row key是用来检索记录的主键。访问hbase table中的行,只有三种方式: (1.1) 通过单个row key访问 (1.2) 通过row key的range (1.3) 全表扫描 Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组。 存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性) 注意: 字典序对int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保持整形的自然序,行键必须用0作左填充。 行的一次读写是原子操作 (不论一次读写多少列)。这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为。 (2) 列族 column family hbase表中的每个列,都归属与某个列族。列族是表的chema的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如courses:history , courses:math 都属于 courses 这个列族。 访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。 (3) 单元 Cell HBase中通过row和columns确定的为一个存贮单元称为cell。由{row key, column( =<family> + <label>), version} 唯一确定的单元。cell中的数据是没有类型的,全部是字节码形式存贮。 (4) 时间戳 timestamp 每个cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。 为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。 3. HBase shell的基本用法 hbase提供了一个shell的终端给用户交互。使用命令hbase shell进入命令界面。通过执行 help可以看到命令的帮助信息。 以网上的一个学生成绩表的例子来演示hbase的用法。 name grad course math art Tom 5 97 87 Jim 4 89 80 这里grad对于表来说是一个只有它自己的列族,course对于表来说是一个有两个列的列族,这个列族由两个列组成math和art,当然我们可以根据我们的需要在course中建立更多的列族,如computer,physics等相应的列添加入course列族。 (1)建立一个表scores,有两个列族grad和courese hbase(main):001:0> create 'scores','grade', 'course' 可以使用list命令来查看当前HBase里有哪些表。使用describe命令来查看表结构。(记得所有的表明、列名都需要加上引号) (2)按设计的表结构插入值: put 'scores','Tom','grade:','5' put 'scores','Tom','course:math','97' put 'scores','Tom','course:art','87' put 'scores','Jim','grade','4' put 'scores','Jim','course:','89' put 'scores','Jim','course:','80' 这样表结构就起来了,其实比较自由,列族里边可以自由添加子列很方便。如果列族下没有子列,加不加冒号都是可以的。 put命令比较简单,只有这一种用法: hbase> put 't1', 'r1', 'c1', 'value', ts1 t1指表名,r1指行键名,c1指列名,value指单元格值。ts1指时间戳,一般都省略掉了。 (3)根据键值查询数据 get 'scores','Jim' get 'scores','Jim','grade' 可能你就发现规律了,HBase的shell操作,一个大概顺序就是操作关键词后跟表名,行名,列名这样的一个顺序,如果有其他条件再用花括号加上。 get有用法如下: hbase> get 't1', 'r1' hbase> get 't1', 'r1', {TIMERANGE => [ts1, ts2]} hbase> get 't1', 'r1', {COLUMN => 'c1'} hbase> get 't1', 'r1', {COLUMN => ['c1', 'c2', 'c3']} hbase> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1} hbase> get 't1', 'r1', {COLUMN => 'c1', TIMERANGE => [ts1, ts2], VERSIONS => 4} hbase> get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1, VERSIONS => 4} hbase> get 't1', 'r1', 'c1' hbase> get 't1', 'r1', 'c1', 'c2' hbase> get 't1', 'r1', ['c1', 'c2'] (4)扫描所有数据 scan 'scores' 也可以指定一些修饰词:TIMERANGE, FILTER, LIMIT, STARTROW, STOPROW, TIMESTAMP, MAXLENGTH,or COLUMNS。没任何修饰词,就是上边例句,就会显示所有数据行。 例句如下: hbase> scan '.META.' hbase> scan '.META.', {COLUMNS => 'info:regioninfo'} hbase> scan 't1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'} hbase> scan 't1', {COLUMNS => 'c1', TIMERANGE => [1303668804, 1303668904]} hbase> scan 't1', {FILTER => "(PrefixFilter ('row2') AND (QualifierFilter (>=, 'binary:xyz'))) AND (TimestampsFilter ( 123, 456))"} hbase> scan 't1', {FILTER => org.apache.hadoop.hbase.filter.ColumnPaginationFilter.new(1, 0)} 过滤器filter有两种方法指出: a. Using a filterString - more information on this is available in the Filter Language document attached to the HBASE-4176 JIRA b. Using the entire package name of the filter. 还有一个CACHE_BLOCKS修饰词,开关scan的缓存的,默认是开启的(CACHE_BLOCKS=>true),可以选择关闭(CACHE_BLOCKS=>false)。 (5)删除指定数据 delete 'scores','Jim','grade' delete 'scores','Jim' 删除数据命令也没太多变化,只有一个: hbase> delete 't1', 'r1', 'c1', ts1 另外有一个deleteall命令,可以进行整行的范围的删除操作,慎用! 如果需要进行全表删除操作,就使用truncate命令,其实没有直接的全表删除命令,这个命令也是disable,drop,create三个命令组合出来的。 (6)修改表结构 disable 'scores' alter 'scores',NAME=>'info' enable 'scores' alter命令使用如下(如果无法成功的版本,需要先通用表disable): a、改变或添加一个列族: hbase> alter 't1', NAME => 'f1', VERSIONS => 5 b、删除一个列族: hbase> alter 't1', NAME => 'f1', METHOD => 'delete' hbase> alter 't1', 'delete' => 'f1' c、也可以修改表属性如MAX_FILESIZE MEMSTORE_FLUSHSIZE, READONLY,和 DEFERRED_LOG_FLUSH: hbase> alter 't1', METHOD => 'table_att', MAX_FILESIZE => '134217728' d、可以添加一个表协同处理器 hbase> alter 't1', METHOD => 'table_att', 'coprocessor'=> 'hdfs:///foo.jar|com.foo.FooRegionObserver|1001|arg1=1,arg2=2' 一个表上可以配置多个协同处理器,一个序列会自动增长进行标识。加载协同处理器(可以说是过滤程序)需要符合以下规则: [coprocessor jar file location] | class name | [priority] | [arguments] e、移除coprocessor如下: hbase> alter 't1', METHOD => 'table_att_unset', NAME => 'MAX_FILESIZE' hbase> alter 't1', METHOD => 'table_att_unset', NAME => 'coprocessor$1' f、可以一次执行多个alter命令: hbase> alter 't1', {NAME => 'f1'}, {NAME => 'f2', METHOD => 'delete'} (7)统计行数: hbase> count 't1' hbase> count 't1', INTERVAL => 100000 hbase> count 't1', CACHE => 1000 hbase> count 't1', INTERVAL => 10, CACHE => 1000 count一般会比较耗时,使用mapreduce进行统计,统计结果会缓存,默认是10行。统计间隔默认的是1000行(INTERVAL)。 (8)disable 和 enable 操作 很多操作需要先暂停表的可用性,比如上边说的alter操作,删除表也需要这个操作。disable_all和enable_all能够操作更多的表。 (9)表的删除 先停止表的可使用性,然后执行删除命令。 drop 't1' 以上是一些常用命令详解,具体的所有hbase的shell命令如下,分了几个命令群,看英文是可以看出大概用处的,详细的用法使用help "cmd" 进行了解。 COMMAND GROUPS: Group name: general Commands: status, version Group name: ddl Commands: alter, alter_async, alter_status, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, is_disabled, is_enabled, list, show_filters Group name: dml Commands: count, delete, deleteall, get, get_counter, incr, put, scan, truncate Group name: tools Commands: assign, balance_switch, balancer, close_region, compact, flush, hlog_roll, major_compact, move, split, unassign, zk_dump Group name: replication Commands: add_peer, disable_peer, enable_peer, list_peers, remove_peer, start_replication, stop_replication Group name: security Commands: grant, revoke, user_permission 4. hbase shell脚本 既然是shell命令,当然也可以把所有的hbase shell命令写入到一个文件内,想linux shell脚本程序那样去顺序的执行所有命令。如同写linux shell,把所有hbase shell命令书写在一个文件内,然后执行如下命令即可: $ hbase shell test.hbaseshell 原文来自: 中国大数据http://www.thebigdata.cn/HBase/6942.html 本文转自 ChinaUnicom110 51CTO博客,原文链接:http://blog.51cto.com/xingyue2011/1936524

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册