第一章:Java 环境配置
一、dos命令行
dir : 列出当前目录下的文件以及文件夹
md : 创建目录
rd : 删除目录
cd : 进入指定目录 // cd jdk*进入到jdk开头的目录
cd.. : 退回到上一级目录cd/ : 退回到根目录
del : 删除文件
exit : 推出dos命令行
help:显示所有命令
help cd :显示该cd命令的用法
Javac -version 查看jdk的版本
二、什么是JRE,JDK
JRE(Java Runtime Environment Java运行环境)
包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
JDK(Java Development Kit Java开发工具包)
JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。
其中的开发工具:编译工具() 打包工具()等
三、配置环境变量path
在dos命令行中敲入javac,出现错误提示。
错误原理:当前执行的程序在当前目录下如果不存在,windows系统会在系统中已有的一个名为path的环境变量指定的目录中查找。如果还没有找到,就出现以上的错误提示。
所以进入到 jdk安装路径\bin目录下,执行javac。看到javac参数提示信息。
根据windows系统在查找可执行程序的原理,可以将java工具所在路径定义到path环境变量中,让系统帮我们去找运行执行的程序。如notepad记事本程序就是放在path环环境变量下,所以在命令行可以直接输入notepad打开记事本程序。
编辑path环境变量,在变量值开始处加上java工具所在目录,后面用 “ ; ”和其他值分隔开即可。
临时配置方式:通过dos命令中set命令完成
set :用于查看本机的所有环境变量的信息。
set 变量名 :查看具体一个环境变量的值。
set 变量名=:清空一个环境变量的值。
set 变量名=具体值 :给指定变量定义具体值。
想要在原有环境变量值基础上添加新值呢?
首先,通过%变量名%操作符获取到原有环境变量的值。
然后加上新值后在定义给该变量名即可
举例:给path环境变量加入新值
set path=新值;%path%
注意:这种配置方式只在当前dos窗口有效。窗口关闭,配置消失。
为了不因为jdk的目录或者目录名称的改变,而不断更改path的值,而导致对path变量值的误操作,可以通过以下技巧完成。
新创建一个环境变量 JAVA_HOME 记录住jdk的目录。
在path中通过%%动态的获取JAVA_HOME的值即可。
JAVA_HOME=F:\jdk1.6.0_01
path=%JAVA_HOME%\bin;%path%
%path%:动态获取path环境变量的值。
%JAVA_HOME%:动态获取名称为JAVA_HOME环境变量的值。
四、配置classpath
既然class文件(字节码文件)是java的运行文件,可不可以实现,在任意目录下都可以执行某一目录下的class文件呢?
根据path环境变量的原理,可以定义一个名为classpath环境变量,将要运行的class文件所在目录定义在该变量中。
例:set classpath=c:\
classpath变量值是java类文件的目录
path变量是windows程序文件的目录
JVM查找类文件的顺序:
如果没有配置classpath环境变量,JVM只在当前目录下查找要运行的类文件。
如果配置了classpath环境,JVM会先在classpath环境变量值的目录中查找要运行的类文件。
值的结尾处如果加上分号,那么JVM在classpath目录下没有找到要指定的类文件,会在当前目录下在查找一次。
值的结尾出如果没有分号,那么JVM在classpath目录下没有找到要指定的类文件,不会在当前目录下查找,即使当前目录下有,也不会运行。
建议:配置classpath环境变量时,值的结尾处不要加分号,如果需要访问当前目录可以用 “.”表示。
第二章:String类
一、String构造函数
char[] arr = {'w','a','p','q','x'};
String s = new String(arr,1,3);
("s="+s);//apq
String s = new String();//等效于String s = ""; 不等效String s = null;
byte[] arr = {97,66,67,68};
String s1 = new String(arr);
("s1="+s1);//aBCD
二、String面试题
String s = "abc";//创建一个字符串对象在常量池中。
String s1 = new String("abc");//在堆内存中创建两个对象:一个new出来的对象,一个字符串对象。
(s==s1);//false
((s1));//string类中的equals复写Object中的equals建立了string类自己的判断字符串对象是否相同的依据。其实就是比较字符串内容。
字符串常量池的特点:池中没有就建立,池中有,直接用。
String s = "abc";//"abc"存储在字符串常量池中。
String s1 = "abc";
(s==s1);//true
三、String常用方法
* 1,获取:
* 1.1 获取字符串中字符的个数(长度).
* int length();
* 1.2 根据位置获取字符。
* char charAt(int index);
* 1.3 根据字符获取在字符串中的第一次出现的位置.
* int indexOf(int ch)
* int indexOf(int ch,int fromIndex):从指定位置进行ch的查找第一次出现位置
* int indexOf(String str);
* int indexOf(String str,int fromIndex);
* 根据字符串获取在字符串中的第一次出现的位置.从后查找
* int lastIndexOf(int ch)
* int lastIndexOf(int ch,int fromIndex):从指定位置进行ch的查找第一次出现位置
* int lastIndexOf(String str);
* int lastIndexOf(String str,int fromIndex);
* 1.4 获取字符串中一部分字符串。也叫子串.
* String substring(int beginIndex, int endIndex)//包含begin 不包含end 。
* String substring(int beginIndex);
String s = "abcdae";
("length:"+());//6
("char:"+(2));//c//StringIndexOutOfBoundsException
("index:"+('k'));//0//-1 我们可以根据-1,来判断该字符或者字符串是否存在。
("lastIndex:"+('a'));//4
("substring:"+(2,4));//cd
* 2,转换。
* 2.1 将字符串变成字符串数组(字符串的切割)
* String[] split(String regex):涉及到正则表达式.
* 2.2 将字符串变成字符数组。
* char[] toCharArray();
* 2.3 将字符串变成字节数组。
* byte[] getBytes();
* 2.4 将字符串中的字母转成大小写。
* String toUpperCase():大写
* String toLowerCase():小写
* 2.5 将字符串中的内容进行替换
* String replace(char oldch,char newch);
* String replace(String s1,String s2);
* 2.6 将字符串两端的空格去除。
* String trim();
* 2.7 将字符串进行连接 。
* String concat(string);//类似“+”
* 2.8 将任意类型转换为字符串。
* String valueOf("任意类型");
String s = "张三,李四,王五";//"张三.李四.王五"
String[] arr = (",");//("\\.");
for (int i = 0; i < ; i++) {
(arr[i] + " ");//张三 李四 王五
}
char[] chs = ();
for (int i = 0; i < ; i++) {
(chs[i]+" ");//张 三 , 李 四 , 王 五
}
s = "ab你";
byte[] bytes = ();
for (int i = 0; i < ; i++) {
(bytes[i]+ " ");//97 98 -60 -29//“你”占两个字节,且第一个2进制位为1,所以都是负数,gb18030>gbk>gb2312
}
("Ab你".toUpperCase());//AB你
("Ab你".toLowerCase());//ab你
("java".replace('a','o'));//jovo
("java".replace('q','o'));//java
String s1 = "java";
String s2 = ('q', 'z');//java
(s1==s2);//true
("-"+" ab c ".trim()+"-");//-ab c-
((4)+1);//41
(""+4+1);//41
* 3,判断
* 3.1 两个字符串内容是否相同啊?
* boolean equals(Object obj);
* boolean equalsIgnoreCase(string str);忽略大小写比较字符串内容。
* 3.2 字符串中是否包含指定字符串?
* boolean contains(string str);
* 3.3 字符串是否以指定字符串开头。是否以指定字符串结尾。
* boolean startsWith(string);
* boolean endsWith(string);
String s = "abc";
(("ABC"));//false
(("ABC".toLowerCase()));//true
(("ABC"));//true
(("cc"));//false
String str = "";
(("Array"));//true
((".java"));//true
(("Demo"));//true
* 4,比较。
* 4.1比较字符串
* int compareTo(string);
* int compareToIgnoreCase(string);//忽略大小写
("abc".compareTo("aqz"));//-3
四、练习
* 1,给定一个字符串数组。按照字典顺序进行从小到大的排序。
* {"nba","abc","cba","zz","qq","haha"}
String[] arr = {"nba","abc","cba","zz","qq","haha"};
("排序前的");
for(int x=0;x<;x++){
(arr[x]+" ");
}
("");
for(int i=0;i<-1;i++){//冒泡排序
for(int j=0;j<-1-i;j++){
if(arr[j].compareTo(arr[j+1])>0){
String t = arr[j];
arr[j] = arr[j+1] ;
arr[j+1] = t;
}
}
}
("排序后的");
for(int x=0;x<;x++){
(arr[x]+" ");
}
排序前的
nba abc cba zz qq haha
排序后的
abc cba haha nba qq zz
* 2,一个子串nba在整串中出现的次数。
* "nbaernbatynbauinbaopnba"
public static void main(String[] args) {
String str ="nbaernbatynbauinbaopnba";
String key = "nba";
int count = getKeyCount(str,key);
("count="+count);//count=5
}
private static int getKeyCount(String str, String key) {
int count = 0;
int index = 0;
while((index=(key))!=-1){
str = (index+());
count++;
}
return count;
}
* 3,两个字符串中最大相同的子串。
* "qwerabcdtyuiop"
* "xcabcdvbn"
public static void main(String[] args) {
String s1 = "qwerabcdtyuiop";
String s2 = "xcabcdvbn";
String s = getMaxSubstring(s2, s1);
("s=" + s);
}
public static String getMaxSubstring(String s1, String s2) {
String max = null,min = null;
max = (()>())?s1:s2;
min = (s1)?s2:s1;
("max="+max);
("min="+min);
for (int i = 0; i < (); i++) {
for(int a = 0,b = ()-i; b != ()+1; a++,b++){
String sub = (a, b);
// (sub);
if((sub))
return sub;
}
}
return null;
}
* 4,模拟一个trim功能一致的方法。去除字符串两端的空白
String s = " ab c ";
s = myTrim(s);
("-" + s + "-");
}
public static String myTrim(String s) {
int start = 0, end = () - 1;
while (start <= end && (start) == ' ') {
start++;
}
while (start <= end && (end) == ' ') {
end--;
}
return (start, end + 1);
}
第三章:StringBuffer和StringBuilder类
一、常用方法
/*
* StringBuffer:就是字符串缓冲区。
* 用于存储数据的容器。
* 特点:
* 1,长度的可变的。
* 2,可以存储不同类型数据。
* 3,最终要转成字符串进行使用。
* 4,可以对字符串进行修改。
*
* 1,添加:
* StringBuffer append(data);
* StringBuffer insert(index,data);
* 2,删除:
* StringBuffer delete(start,end):包含头,不包含尾。
* StringBuffer deleteCharAt(int index):删除指定位置的元素
* 3,查找:
* char charAt(index);
* int indexOf(string);
* int lastIndexOf(string);
* 4,修改:
* StringBuffer replace(start,end,string);
* void setCharAt(index,char);
* StringBuffer reverse();字符串反转
*
* 增删改查 C(create)U(update)R(read)D(delete)
*
二、StringBulider
* jdk1.5以后出现了功能和StringBuffer一模一样的对象。就是StringBuilder
*
* 不同的是:
* StringBuffer是线程同步的。通常用于多线程。
* StringBuilder是线程不同步的。通常用于单线程。 它的出现提高效率。
*
* jdk升级:
* 1,简化书写。
* 2,提高效率。
* 3,增加安全性。
加锁
class StringBuffer jdk1.0
{
object lock;
public StirngBuffer append(int x)
{
synchronized(lock)
{
}
}
public synchronized StringBuffer delete(int start,int end)
{
synchronized(lock)
{
}
}
}
第四章:多线程
一、概述
进程:正在进行中的程序(直译).
线程:就是进程中一个负责程序执行的控制单元(执行路径)
一个进程中可以多执行路径,称之为多线程。
一个进程中至少要有一个线程。
开启多个线程是为了同时运行多部分代码。
每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。
多线程好处:解决了多部分同时运行的问题。
多线程的弊端:线程太多导致效率的降低。
其实应用程序的执行都是cpu在做着快速的切换完成的。这个切换是随机的。
JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。
1,执行main函数的线程,
该线程的任务代码都定义在main函数中。
2,负责垃圾回收的线程。
finalize()方法:当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。 finalize 的常规协定是:当 JavaTM 虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法。
new Demo();匿名对象使用完后没有被引用,就会被垃圾回收器回收,回收时会调用该类的finalize() 方法。
示例:
class Demo extends Object
{
public void finalize()
{
("demo ok");
}
}
class ThreadDemo
{
public static void main(String[] args)
{
new Demo();
new Demo();
new Demo();
();//显示调用垃圾回收器
("Hello World!");
}
}//垃圾回收器和main方法是两个线程,所有有多个运行结果
二、线程状态
三、创建线程
1、继承Thread类
步骤:
(1)定义一个类继承Thread类。
(2)覆盖Thread类中的run方法。
(3)直接创建Thread的子类对象创建线程。
(4)调用start方法开启线程并调用线程的任务run方法执行。
(线程的名字:Thread的getName()获取到线程的名称:Thread-编号(从0开始);主线程的名字为main。().getName()获取当前线程的名字)
问:调用run和调用start有什么区别?
run()只是直接调用了该方法,没有开启一个新线程,start()才是开启一个新线程。
示例:
class Demo extends Thread
{
private String name;
Demo(String name)
{
//super(name);//改变线程名通过调用父类的构造函数Thread(String name);
= name;
}
public void run()
{
for(int x=0; x<10; x++)
{
(name+"....x="+x+".....name="+getName());
}
}
}
class ThreadDemo2
{
public static void main(String[] args)
{
Demo d1 = new Demo("旺财");
Demo d2 = new Demo("xiaoqiang");
();//开启线程,调用run方法。
();
("over...."+().getName());
}
}
2、实现Runnable接口
步骤:
(1)定义类实现Runnable接口。
(2)覆盖接口中的run方法,将线程的任务代码封装到run方法中。
(3)通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
(4)调用线程对象的start方法开启线程。
示例:
class Demo implements Runnable//extends Fu //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。
//通过接口的形式完成。
{
public void run()
{
show();
}
public void show()
{
for(int x=0; x<20; x++)
{
(().getName()+"....."+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
();
();
}
}
3、实现Runnable接口的好处:
(1),将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务的封装成对象。
(2),避免了java单继承的局限性。
所以,创建线程的第二种方式较为常用。
4、简化模拟Thread类和两种线程的创建
class Thread
{
private Runnable r;
Thread()
{
}
Thread(Runnable r)
{
= r;
}
public void run()
{
if(r!=null)
();
}
public void start()
{
run();
}
}
class ThreadImpl implements Runnable
{
public void run()
{
("runnable run");
}
}
ThreadImpl i = new ThreadImpl();
Thread t = new Thread(i);
();
class SubThread extends Thread
{
public void run()
{
("hahah");
}
}
SubThread s = new SubThread();
();
四、线程安全问题产生的原因:
(1)多个线程在操作共享的数据。
(2)操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。
解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,
其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象)//可以直接传入个String对象。synchronized("hzb"){}
{
需要被同步的代码 ;
}
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。
*/
class Ticket implements Runnable//extends Thread
{
private int num = 100;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
try{(10);}catch (InterruptedException e){}
(().getName()+".....sale...."+num--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();//创建一个线程任务对象。
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
();
();
();
();
/*
Ticket t1 = new Ticket();
// Ticket t2 = new Ticket();
// Ticket t3 = new Ticket();
// Ticket t4 = new Ticket();
();
();//一个线程不能开启两次,会抛出无效线程状态异常
();
();
*/
}
}
五、同步的两种方式
1、同步代码块
2、同步函数:使用的锁是this,直接在函数前面加关键字synchronized
public synchronized void add(int num){};
问题:同步函数和同步代码块的区别?
同步函数的锁是固定的this,同步代码块的锁是任意的对象。
建议使用同步代码块。
示例:同步代码块和同步函数实现火车售票
class Ticket implements Runnable
{
private int num = 100;
// Object obj = new Object();
boolean flag = true;
public void run()
{
// ("this:"+this);
if(flag)
while(true)
{
synchronized(this)
{
if(num>0)
{
try{(10);}catch (InterruptedException e){}
(().getName()+".....obj...."+num--);
}
}
}
else
while(true)
();
}
public synchronized void show()
{
if(num>0)
{
try{(10);}catch (InterruptedException e){}
(().getName()+".....function...."+num--);
}
}
}
class SynFunctionLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
// ("t:"+t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
();
try{(10);}catch(InterruptedException e){}
= false;
();
}
}
静态的同步函数使用的锁是:该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前类名.class 表示。
,()
六、单例模式
饿汉式
class Single{
private static final Single s = new Single();
private Single(){}
public static Single getInstance(){
return s;
}
}
懒汉式
class Single{
private staic Single s = null;
private Single(){}
public static Single getInstance(){
if(s == null)
s = new Single();
return s;
}
}
加入同步为了解决多线程安全问题。
加入双重判断是为了解决效率问题。
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized() //静态函数同步锁要这样写
{
if(s==null)
// -->0 -->1
s = new Single();
}
}
return s;
}
}
七、线程死锁
死锁:常见情景之一:同步的嵌套。
死锁示例:
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
= flag;
}
public void run()
{
if(flag)
{
while(true)
synchronized()
{
(().getName()+"..if locka....");
synchronized() {
(().getName()+"..if lockb....");
}
}
}
else
{
while(true)
synchronized()
{
(().getName()+"..else lockb....");
synchronized()
{
(().getName()+"..else locka....");
}
}
}
}
}
class MyLock
{
public static final Object locka = new Object();
public static final Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Test a = new Test(true);
Test b = new Test(false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
();
();
}
}
八、面试题
class Test implements Runnable
{
public void run(Thread t)
{}
}
//如果错误 错误发生在哪一行?
答:错误在第一行,应该被abstract修饰
new Thread(new Runnable()
{
public void run()
{
("runnable run");
}
})
{
public void run()
{
("subThread run");
}
}.start();//输出结果为subThread run
在main函数中创建3个线程:
new Thread()
{
public void run()
{
for(int x=0; x<50; x++)
{
(().getName()+"....x="+x);
}
}
}.start();
for(int x=0; x<50; x++)
{
(().getName()+"....y="+x);
}
Runnable r = new Runnable()
{
public void run()
{
for(int x=0; x<50; x++)
{
(().getName()+"....z="+x);
}
}
};
new Thread(r).start();
九、等待/唤醒机制
涉及的方法:
1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。
这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。、
为什么操作线程的方法wait notify notifyAll定义在了Object类中?
因为这些方法是监视器的方法。监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。
//资源
class Resource
{
String name;
String sex;
boolean flag = false;
}
//输入
class Input implements Runnable
{
Resource r ;
// Object obj = new Object();
Input(Resource r)
{
= r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
if()
try{();}catch(InterruptedException e){}
if(x==0)
{
= "mike";
= "nan";
}
else
{
= "丽丽";
= "女女女女女女";
}
= true;
();
}
x = (x+1)%2;
}
}
}
//输出
class Output implements Runnable
{
Resource r;
// Object obj = new Object();
Output(Resource r)
{
= r;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!)
try{();}catch(InterruptedException e){}
(+"....."+);
= false;
();
}
}
}
}
class ResourceDemo2
{
public static void main(String[] args)
{
//创建资源。
Resource r = new Resource();
//创建任务。
Input in = new Input(r);
Output out = new Output(r);
//创建线程,执行路径。
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
();
();
}
}
优化后的代码:
class Resource
{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex)
{
if(flag)
try{();}catch(InterruptedException e){}
= name;
= sex;
flag = true;
();
}
public synchronized void out()
{
if(!flag)
try{();}catch(InterruptedException e){}
(name+"...+...."+sex);
flag = false;
notify();
}
}
//输入
class Input implements Runnable
{
Resource r ;
// Object obj = new Object();
Input(Resource r)
{
= r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
{
("mike","nan");
}
else
{
("丽丽","女女女女女女");
}
x = (x+1)%2;
}
}
}
//输出
class Output implements Runnable
{
Resource r;
// Object obj = new Object();
Output(Resource r)
{
= r;
}
public void run()
{
while(true)
{
();
}
}
}
class ResourceDemo3
{
public static void main(String[] args)
{
//创建资源。
Resource r = new Resource();
//创建任务。
Input in = new Input(r);
Output out = new Output(r);
//创建线程,执行路径。
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
();
();
}
}
十、wait和sleep的区别
1,wait可以指定时间也可以不指定。
sleep必须指定时间。
2,在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
十一、多线程总结:
1,进程和线程的概念。
|--进程:
|--线程:
2,jvm中的多线程体现。
|--主线程,垃圾回收线程,自定义线程。以及他们运行的代码的位置。
3,什么时候使用多线程,多线程的好处是什么?创建线程的目的?
|--当需要多部分代码同时执行的时候,可以使用。
4,创建线程的两种方式。★★★★★
|--继承Thread
|--步骤
|--实现Runnable
|--步骤
|--两种方式的区别?
5,线程的5种状态。
对于执行资格和执行权在状态中的具体特点。
|--被创建:
|--运行:
|--冻结:
|--临时阻塞:
|--消亡:
6,线程的安全问题。★★★★★
|--安全问题的原因:
|--解决的思想:
|--解决的体现:synchronized
|--同步的前提:但是加上同步还出现安全问题,就需要用前提来思考。
|--同步的两种表现方法和区别:
|--同步的好处和弊端:
|--单例的懒汉式。
|--死锁。
7,线程间的通信。等待/唤醒机制。
|--概念:多个线程,不同任务,处理同一资源。
|--等待唤醒机制。使用了锁上的 wait notify notifyAll. ★★★★★
|--生产者/消费者的问题。并多生产和多消费的问题。 while判断标记。用notifyAll唤醒对方。 ★★★★★
|--JDK1.5以后出现了更好的方案,★★★
Lock接口替代了synchronized
Condition接口替代了Object中的监视方法,并将监视器方法封装成了Condition
和以前不同的是,以前一个锁上只能有一组监视器方法。现在,一个Lock锁上可以多组监视器方法对象。
可以实现一组负责生产者,一组负责消费者。
|--wait和sleep的区别。★★★★★
8,停止线程的方式。
|--原理:
|--表现:--中断。
9,线程常见的一些方法。
|--setDaemon()
|--join();
|--优先级
|--yield();
|--在开发时,可以使用匿名内部类来完成局部的路径开辟。
第五章:基本数据类型包装类
一、基本数据类型对象包装类。
* 为了方便操作基本数据类型值,将其封装成了对象,在对象中定义了属性和行为丰富了该数据的操作。
* 用于描述该对象的类就称为基本数据类型对象包装类。
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* char Character
* boolean Boolean
*
* 该包装对象主要用基本类型和字符串之间的转换。
*
* 基本类型--->字符串
* 1,基本类型数值+""
* 2,用String类中的静态方法valueOf(基本类型数值);
*
* 字符串--->基本类型
* 1,使用包装类中的静态方法 xxx parseXxx("xxx类型的字符串");*****
* int parseInt("intstring");
* long parseLong("longstring");
* boolean parseBoolean("booleanstring");
* 只有Character没有parse方法
* 2,如果字符串被Integer进行对象的封装。Integer i = new Integer("123"); ()
* 可使用另一个非静态的方法,intValue();
* 将一个Integer对象转成基本数据类型值。
*
* 整数具备不同的进制体现。
* 十进制-->其他进制。
* toBinaryString// (60)//转换为2进制
* toOctalString//(60)//转换为8进制
* toHexString//(60)//转换为16进制
* (60,4)//转换为任意进制
* 其他进制-->十进制。
* parseInt("string",radix)//("3c",16)
二、自动拆箱装箱面试题
Integer a = new Integer(128);//new Integer(127);//也一样
Integer b = new Integer(128);//new Integer(127);//也一样
(a==b);//false
((b));//true
jdk1.5以后,自动装箱,如果装箱的是一个字节,那么该数据会被共享不会重新开辟空间。一个字节是-128--127,如果是该范围内,即
Integer x = 127;
Integer y = 127;
(x==y);//输出为true
((y));//true
而
Integer x = 128;
Integer y = 128;
(x==y);//输出为false
((y));//true
项目经验:比较两个数是否相同,最好统一用(y)的形式
三、练习题
() 排序方法
* 对一个字符串中的数值进行从小到大的排序。
*
* "20 78 9 -7 88 36 29"
*
* 思路:
* 1,排序,我很熟。可是我只熟int。
* 2,如何获取到这个字符串中的这些需要排序的数值?
* 发现这个字符串中其实都是空格来对数值进行分隔的。
* 所以就想到用字符串对象的切割方法将大串变成多个小串。
* 3,数值最终变成小字符串,怎么变成一个int数呢?
* 字符串-->基本类型 可以使用包装类。
private static final String SPACE_SEPARATOR = " ";
public static void main(String[] args) {
String numStr = "20 78 9 -7 88 36 29";
(numStr);
numStr = sortStringNumber(numStr);
(numStr);
}
public static String sortStringNumber(String numStr) {
//1,将字符串变成字符串数组。
String[] str_arr = stringToArray(numStr);
//2,将字符串数组变成int数组。
int[] num_arr = toIntArray(str_arr);
//3,对int数组排序。
mySortArray(num_arr);
//4,将排序后的int数组变成字符串。
String temp = arrayToString(num_arr);
return temp;
}
public static String arrayToString(int[] num_arr) {
StringBuilder sb = new StringBuilder();
for(int x = 0; x<num_arr.length; x++){
if(x!=num_arr.length-1)
(num_arr[x]+SPACE_SEPARATOR);
else
(num_arr[x]);
}
return ();
}
public static void mySortArray(int[] num_arr) {
(num_arr);
}
public static int[] toIntArray(String[] str_arr) {
int[] arr = new int[str_arr.length];
for (int i = 0; i < ; i++) {
arr[i] = (str_arr[i]);
}
return arr;
}
public static String[] stringToArray(String numStr) {
String[] str_arr = (SPACE_SEPARATOR);
return str_arr;
}
第六章:集合
一、集合类的由来:
对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。
就使用集合容器进行存储。
集合特点:
1,用于存储对象的容器。
2,集合的长度是可变的。
3,集合中不可以存储基本数据类型值。
集合容器因为内部的数据结构不同,有多种具体容器。
不断的向上抽取,就形成了集合框架。框架的顶层就是Collection接口:
集合框架图:
这里的有序是指插入abc,bca,aca就输出abc,bca,aca
Collection:接口,单值
——List:接口,有序有重复
————ArrayList:有序有重复,内部是数组结构
————LinkedList:有序有重复,内部是链表结构
——Set:接口,无序无重复
————HashSet:无序无重复
——————LinkedHashSet:有序无重复
————TreeSet:无序无重复,但元素内部数据会自己排序
Map:接口,键值对
——HashMap:无序
————LinkedHashMap:有序
——TreeMap:无序,但元素内部数据会自己排序
二、Collection的常见方法:
1,添加。
boolean add(Object obj):
boolean addAll(Collection coll):
2,删除。
boolean remove(object obj):
boolean removeAll(Collection coll);//清除和参数集合中相同的元素
//coll1=["a","b","c","d"];coll2=["c","d","e"];
//(coll2);(coll1);//[a,b]
void clear();//清除所有元素
3,判断:
boolean contains(object obj):
boolean containsAll(Colllection coll);
boolean isEmpty():判断集合中是否有元素。
4,获取:
int size():
Iterator iterator():取出元素的方式:迭代器。
该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。
所以该迭代器对象是在容器中进行内部实现的。
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,
也就是iterator方法。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
Iterator it = ();
while(()){
(());
}
for(Iterator it = (); (); ){
(());
}//真实开发用这方法,省内存空间
for(Object obj : coll){
String s = (String) obj;
(s);
}
5,其他:
boolean retainAll(Collection coll);取交集。
Object[] toArray():将集合转成对象数组。
三、Collection
|--List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
|--Set:元素不能重复,无序。
List:特有的常见方法:有一个共性特点就是都可以操作角标。
1,添加
void add(index,element);
void add(index,collection);
2,删除;
Object remove(index):
3,修改:
Object set(index,element);
4,获取:
Object get(index);
int indexOf(object);
int lastIndexOf(object);
List subList(from,to);包含头不包含尾
四、List:
|--Vector:内部是数组数据结构,是同步的。增删,查询都很慢!
|--ArrayList:内部是数组数据结构,是不同步的。替代了Vector。查询的速度快。
|--LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。
List list = new ArrayList();
("abc1");
("abc2");
("abc3");
("list:"+list);
ListIterator it = ();//获取列表迭代器对象
//它可以实现在迭代过程中完成对元素的增删改查。
//注意:只有list集合具备该迭代功能.
while(()){
Object obj = ();
if(("abc2")){
("abc9");
}
}
while(()){
("previous:"+());
}
("list:"+list);
五、LinkedList
addFirst();
addLast():
jdk1.6
offerFirst();
offetLast();
getFirst();.//获取但不移除,如果链表为空,抛出NoSuchElementException.
getLast();
jdk1.6
peekFirst();//获取但不移除,如果链表为空,返回null.
peekLast():
removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException.
removeLast();
jdk1.6
pollFirst();//获取并移除,如果链表为空,返回null.
pollLast();
自动装箱:当基本数据类型赋值给引用类型时。
六、Set:元素不可以重复,是无序。
Set接口中的方法和Collection一致。
|--HashSet: 内部数据结构是哈希表 ,是不同步的。
如何保证该集合的元素唯一性呢?
是通过对象的hashCode和equals方法来完成对象唯一性的。
如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。
如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。
记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。而存储到ArrayList中,则只需覆盖equals方法。
一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。这是为了建立对象判断是否相同的依据。
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
= name;
= age;
}
@Override
public int hashCode() {//HashSet中判断一个对象中的属性是否都相同需要覆盖的方法
return ()+age*27;
}
@Override
public boolean equals(Object obj) {//HashSet和ArrayList中判断一个对象中的属性是否都相同 //需要覆盖的方法
if(this == obj)
return true;
if(!(obj instanceof Person))
throw new ClassCastException("类型错误");
Person p = (Person)obj;
return () && == ;
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
= age;
}
public String toString(){
return name+":"+age;
}
}
}
* 定义功能去除ArrayList中的重复元素。
public class ArrayListTest2 {
public static void main(String[] args) {
ArrayList al = new ArrayList();
(new Person("lisi1",21));
(new Person("lisi2",22));
(new Person("lisi3",23));
(new Person("lisi4",24));
(new Person("lisi2",22));
(al);
al = getSingleElement(al);
(al);
}
public static ArrayList getSingleElement(ArrayList al) {
//1,定义一个临时容器。
ArrayList temp = new ArrayList();
//2,迭代al集合。
Iterator it = ();
while(()){
Object obj = ();
//3,判断被迭代到的元素是否在临时容器存在。
if(!(obj)){//contains调用的还是equals方法,因为String类中覆盖了equals方法, //所以"abc"跟"abc"是相同的,而Person对象中("zhangsan",23)跟("zhangsan",23)如果没//有覆盖equals方法,是不同的,所以要在Person中覆盖该方法
(obj);
}
}
return temp;
}
}
哈希表确定元素是否相同
1,判断的是两个元素的哈希值是否相同。
如果相同,在判断两个对象的内容是否相同。
2,判断哈希值相同,其实判断的是对象的hashCode的方法。判断内容相同,用的是equals方法。
注意:如果哈希值不同,是不需要判断equals。
if(()== () && (obj))
六、|--TreeSet:可以对Set集合中的元素进行排序。是不同步的。
TreeSet 判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。
TreeSet对元素进行排序的方式一:
让元素自身具备比较功能,元素需要实现Comparable接口。覆盖compareTo方法。
public class Person implements Comparable {
public int compareTo(Object o) {
Person p = (Person)o;
int temp = ;//先年龄后名字
return temp==0?():temp;
// int temp = ();//先名字后年龄
// return temp==0?:temp;
}
}
如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?
可以使用TreeSet集合第二种排序方式二:
让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。
将该类对象作为参数传递给TreeSet集合的构造函数。
public class ComparatorByName implements Comparator {
@Override
public int compare(Object o1, Object o2) {
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int temp = ().compareTo(());
return temp==0?()-(): temp;
// return 1;//有序。
}
}
public class ComparatorByLength implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String s1 = (String)o1;
String s2 = (String)o2;
int temp = ()-();
return temp==0? (s2): temp;
}
}
TreeSet ts = new TreeSet(new ComparatorByName ());
TreeSet ts = new TreeSet(new ComparatorByLength());
七、泛型
jdk1.5出现的安全机制。
好处:
1,将运行时期的问题ClassCastException转到了编译时期。
2,避免了强制转换的麻烦。
<>:什么时候用?当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型传入即可.
其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型 。
泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全。
运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?因为为了兼容运行的类加载器。
泛型的补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。
ArrayList<String> al = new ArrayList<String>();
("abc");
("hahah");
Iterator<String> it = ();
while(()){
String str = ();
(str);
}
八、自定义泛型
public class Tool<QQ>{
private QQ q;
public QQ getObject() {
return q;
}
public void setObject(QQ object) {
= object;
}
* 将泛型定义在方法上。
public <W> void show(W str){
("show : "+());
}
public void print(QQ str){
("print : "+str);
}
* 当方法静态时,不能访问类上定义的泛型,即访问QQ。如果静态方法使用泛型,
* 只能将泛型定义在方法上。
public static <Y> void method(Y obj){
("method:"+obj);
}
}
Tool<String> tool = new Tool<String>();
(new Integer(4));//因为show方法上自定义了泛型,所以可以传任意值
("abc");
("hahah");
("haha");
(new Integer(9));
九、泛型接口
public static void main(String[] args) {
InterImpl in = new InterImpl();
("abc");
InterImpl2<Integer> in2 = new InterImpl2<Integer>();
(5);
}
//泛型接口,将泛型定义在接口上。
interface Inter<T>{
public void show(T t);
}
class InterImpl implements Inter<String>{
public void show(String str){
("show :"+str);
}
}
class InterImpl2<Q> implements Inter<Q>{
public void show(Q q){
("show :"+q);
}
}
十、泛型的通配符:? 未知类型。
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
("abc");
("hehe");
ArrayList<Integer> al2 = new ArrayList<Integer>();
(5);
(67);
printCollection(al);
printCollection(al2);
}
public static void printCollection(Collection<?> al) {
Iterator<?> it = ();
while(()){
(());
}
十一、泛型的限定:
? extends E: 接收E类型或者E的子类型对象。上限
一般存储对象的时候用。比如 添加元素 addAll.
? super E: 接收E类型或者E的父类型对象。 下限。
一般取出对象的时候用。比如比较器。
十二、集合的一些技巧:
需要唯一吗?
需要:Set
需要制定顺序:
需要: TreeSet
不需要:HashSet
但是想要一个和存储一致的顺序(有序):LinkedHashSet
不需要:List
需要频繁增删吗?
需要:LinkedList
不需要:ArrayList
后缀名就是该集合所属的体系。
前缀名就是该集合的数据结构。
看到array:就要想到数组,就要想到查询快,有角标.
看到link:就要想到链表,就要想到增删快,就要想要 add get remove+frist last的方法
看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法。
看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator 。
而且通常这些常用的集合容器都是不同步的。
十三、Map
一次添加一对元素。Collection 一次添加一个元素。
Map也称为双列集合,Collection集合称为单列集合。
其实map集合中存储的就是键值对。
map集合中必须保证键的唯一性。
常用方法:
1,添加。
value put(key,value):返回前一个和key关联的值,如果没有返回null.
2,删除。
void clear():清空map集合。
value remove(key):删除key指定的键值对并返回这个键值。
3,判断。
boolean containsKey(key):
boolean containsValue(value):
boolean isEmpty();
4,获取。
value get(key):通过键获取值,如果没有该键返回null。
当然可以通过返回null,来判断是否包含指定键。
int size(): 获取键值对的个数。
Map常用的子类:
|--Hashtable :内部结构是哈希表,是同步的。不允许null作为键,null作为值。
|--Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合。
|--HashMap : 内部结构是哈希表,不是同步的。允许null作为键,null作为值。
|--TreeMap : 内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。
取出map中的所有元素的方法。
Map<Integer,String> map = new HashMap<Integer,String>();
(8,"zhaoliu");
(2,"zhaoliu");
(7,"xiaoqiang");
(6,"wangcai");
//1、通过keySet方法获取map中所有的键所在的Set集合,再通过Set的迭代器获取到每一个键,再对每一个键通过map集合的get方法获取其对应的值。
Set<Integer> keySet = ();
Iterator<Integer> it = ();
while(()){
Integer key = ();
String value = (key);
(key+":"+value);
}
//2、将Map转成set进行迭代,用entrySet方法。该方法将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是类型(结婚证)
Set<<Integer, String>> entrySet = ();
Iterator<<Integer, String>> it = ();
while(()){
<Integer, String> me = ();
Integer key = ();
String value = ();
(key+"::::"+value);
}
//3、values方法,只获取所有键对应的值
Collection<String> values = ();
Iterator<String> it2 = ();
while(()){
(());
}
十四、练习:记录字母在字符串中出现次数
* "fdgavcbsacdfs" 获取该字符串中,每一个字母出现的次数。
* 要求打印结果是:a(2)b(1)...;
* 思路:
* 对于结果的分析发现,字母和次数之间存在着映射的关系。而且这种关系很多。
* 很多就需要存储,能存储映射关系的容器有数组和Map集合。
* 关系一方式有序编号吗?没有!
* 那就是使用Map集合。 又发现可以保证唯一性的一方具备着顺序如 a b c ...
* 所以可以使用TreeMap集合。
*
* 这个集合最终应该存储的是字母和次数的对应关系。
*
* 1,因为操作的是字符串中的字母,所以先将字符串变成字符数组。
* 2,遍历字符数组,用每一个字母作为键去查Map集合这个表。
* 如果该字母键不存在,就将该字母作为键 1作为值存储到map集合中。
* 如果该字母键存在,就将该字母键对应值取出并+1,在将该字母和+1后的值存储到map集合中,
* 键相同值会覆盖。这样就记录住了该字母的次数.
* 3,遍历结束,map集合就记录所有字母的出现的次数。
public static void main(String[] args){
String str="fdgavcbsacdfs";
String newStr = sortString(str);
(newStr);
}
private static String sortString(String str) {
StringBuffer newStr = new StringBuffer();
Map<String,Integer> map = new TreeMap<String,Integer>();
char[] ch = ();
for(int x=0;x<;x++){
if(((ch[x]))==null){
((ch[x]), 1);
}else{
Integer value = ((ch[x]))+1;
((ch[x]), value);
}
}
// Set<String> set = ();
// Iterator<String> it = ();
// while(()){
// String ss = ();
// Integer i = (ss);
// (ss);
// ("(");
// (i);
// (")");
// }
Iterator<<String,Integer>> it = ().iterator();
while(()){
<String,Integer> me = ();
(());
("(");
(());
(")");
}
return ();
}
public static void main(String[] args) {
String str = "fdg+avAdc bs5dDa9c-dfs";
String s = getCharCount(str);
(s);
}
public static String getCharCount(String str) {
//将字符串变成字符数组
char[] chs = ();
//定义map集合表。
Map<Character,Integer> map = new TreeMap<Character,Integer>();
for (int i = 0; i < ; i++) {
if(!(chs[i]>='a' && chs[i]<='z' || chs[i]>='A' && chs[i]<='Z'))
// if(!((chs[i])>='a' && (chs[i])<='z'))
continue;
//将数组中的字母作为键去查map表。
Integer value = (chs[i]);
int count = 1;
//判断值是否为null.
if(value!=null){
count = value+1;
}
(chs[i], count);
}
return mapToString(map);
}
private static String mapToString(Map<Character, Integer> map) {
StringBuilder sb = new StringBuilder();
Iterator<Character> it = ().iterator();
while(()){
Character key = ();
Integer value = (key);
(key+"("+value+")");
}
return ();
}
十五、工具类
集合工具类中的方法都是静态方法
(1)Collections 用来操作集合的
1、(list);自然排序
2、(list,new ComparatorByLength());根据比较器中的方法来排序
示例:根据字符串长度来排序
public class ComparatorByLength implements Comparator<String> {
public int compare(String o1, String o2) {
int temp = () - ();
return temp==0?(o2): temp;
}
}
示例:根据字符串自然逆向顺序来排序
public class ComparatorByReverse implements Comparator<String> {
public int compare(String o1, String o2) {
return (o1);
}
}
3、(list,i,j);在指定list列表的指定位置处交换元素
4、(list, "cba", "nba");使用另一个值替换列表中出现的所有某一指定值。用nba替换cba
5、(list);使用默认随机源对指定列表进行置换。每次list中出现的元素顺序都不一样
6、(list, "cc");使用指定元素替换指定列表中的所有元素。
7、(list);根据元素的自然顺序,返回给定 collection 的最大元素。
8、(list,new ComparatorByLength());根据指定比较器产生的顺序,返回给定 collection 的最大元素。
9、(list);反转list中元素顺序
10、int index = (list, "cba");二分查找
11、();返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序
示例:TreeSet<String> ts = new TreeSet<String>(());
12、(new ComparatorByLength());返回一个比较器,它强行逆转指定比较器的顺序。
13、List synList = Collections.synchronizedList(list);返回指定列表支持的同步(线程安全的)列表//synchronizedSet、synchronizedMap
(2)Arrays 用来操作数组的
1、List asList(数组);将数组转成集合
* 好处:可以使用集合的方法操作数组中的元素。如contains()
* 注意:数组的长度是固定的,所以对于集合的增删方法是不可以使用的
* 否则会发生UnsupportedOperationException
2、Object[] toArray()//返回的是对象数组//不是属于Arrays类的,该方法是集合转为数组
<T> T[]toArray(T[] a) //返回的是传入参数类型的数组
List<String> list = new ArrayList<String>();
("abc1");
("abc2");
("abc3");
String[] arr = (new String[()]);
//Object[] obj = ();
3、void sort(String[] a) ;//这里有很多方法重载
(arr);
十六、jdk1.5新特性
1、foreach语句:
* 格式:
* for(类型 变量 :Collection集合|数组)
* {
* }
*
* 传统for和高级for的区别?
* 传统for可以完成对语句执行很多次,因为可以定义控制循环的增量和条件。
*
* 高级for是一种简化形式。
* 它必须有被遍历的目标。该目标要是数组,要么是Collection单列集合。
*
* 对数数组的遍历如果仅仅是获取数组中的元素,可以使用高级for。
* 如果要对数组的角标进行操作建议使用传统for。
int[] arr = {3,1,5,7,4};
for(int i : arr){
(i);
//可以使用高级for遍历map集合吗?不能直接用,但是可以将map转成单列的set,就可以用了。
Map<Integer,String> map = new HashMap<Integer,String>();
(3,"zhagsan");
(1,"wangyi");
(7,"wagnwu");
(4,"zhagsansan");
for(Integer key : ()){
String value = (key);
(key+"::"+value);
}
for(<Integer,String> me : ()){
Integer key = ();
String value = ();
(key+":"+value);
}
2、函数的可变参数
* 其实就是一个数组,但是接收的是数组的元素。
* 自动将这些元素封装成数组。简化了调用者的书写。
* 注意:可变参数类型,必须定义在参数列表的结尾。
int sum = newAdd(5,1,4,7,3);
("sum="+sum);
int sum1 = newAdd(5,1,2,7,3,9,8,7,6);
("sum1="+sum1);
public static int newAdd(int a,int... arr){
int sum = 0;
for (int i = 0; i < ; i++) {
sum+=arr[i];
}
return sum;
}
3、静态导入(基本没用到)
import static .*;//静态导入,其实到入的是类中的静态成员。
sort(list);//(list);简写成该形式
第七章:常用类
一、System类:类中的方法和属性都是静态的。
1、long currentTimeMillis();获取当前时间的毫秒值。
2、("myclasspath", "c:\myclass");给系统设置一些属性信息。这些信息是全局,其他程序都可以使用。
3、示例:
Properties prop = ();//获取系统的属性信息,并存储到了Properties集合中,properties集合中存储的都是String类型的键和值
Set<String> nameSet = ();// 使用它自己的存储和取出的方法来完成元素的操作
for(String name : nameSet){
String value = (name);
(name+"::"+value);
}
}
4、通用换行符:根据系统键获取系统属性值
private static final String LINE_SEPARATOR = ("");
("hello-"+LINE_SEPARATOR+" world");
二、Runtime类
* 没有构造方法摘要,说明该类不可以创建对象。
* 又发现还有非静态的方法。说明该类应该提供静态的返回该类对象的方法。
* 而且只有一个,说明Runtime类使用了单例设计模式。
Runtime r = ();
Process p = ("");
(5000);
();
三、Math类
* 常用的方法:
* ceil():返回大于参数的最小整数。
* floor():返回小于参数的最大整数。
* round():返回四舍五入的整数。
* pow(a,b):a的b次方。
* random() 返回[0,1)中的一个随机值
double d1 = (12.56);//13//负数的话就相反//-12.56=-12
double d2 = (12.56);//12//-12.56=-13
double d3 = (12.56);//13//-12.56=-13
四、Date类
1、开发常用:String currentTime=(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date());
2、日期对象和毫秒值之间的转换
* 毫秒值-->日期对象 :
* 1,通过Date对象的构造方法 new Date(timeMillis);
* 2,还可以通过setTime设置。
* 日期对象-->毫秒值:
* 2,getTime方法。
3、日期格式的字符串和日期对象的转换
* 1,将日期格式的字符串-->日期对象。
* 使用的是DateFormat类中的parse()方法。 SimpleDateFormat是DateFormat的子类
Date date =(new SimpleDateFormat("yyyy---MM---dd")).parse("2012---8---17");
* 2,将日期对象-->日期格式的字符串。
* 使用的是DateFormat类中的format方法。
String currentTime=(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date());
4、练习
* "2012-3-17"到"2012-4-6"
* 中间有多少天?
* 思路:
* 两个日期相减。
* 咋减呢?
* 必须要有两个可以进行减法运算的数。
* 能减可以是毫秒值。如何获取毫秒值?通过date对象。
* 如何获取date对象呢?可以将字符串转成date对象。
* 1,将日期格式的字符串转成Date对象。
* 2,将Date对象转成毫秒值。
* 3,相减,再变成天数
String strBeforeDate="2012-3-17";
String strAfterDate="2012-4-6";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date beforeDate = (strBeforeDate);
Date afterDate = (strAfterDate);
long time=(()- ());
int day = (int)(time/1000/60/60/24);
(day);
五、Calendar类
当前日期是2012-10-27星期六
int year = ();//2012
int month = ()+1;//9+1
int day = (Calendar.DAY_OF_MONTH);//27
int week = (Calendar.DAY_OF_WEEK);//7
最重要的三个方法get(),set(),add()
练习:求出任意一年的2月有多少天
*/
public static void main(String[] args) {
int year = 2012;
showDays(year);
}
public static void showDays(int year) {
Calendar c = ();
(year, 2, 1);//3月1号
(Calendar.DAY_OF_MONTH, -1);//3月1号偏移-1天就是那一年2月的最后一天
showDate(c);
}
public static void showDate(Calendar c) {
int year = ();
int month = ()+1;
int day = (Calendar.DAY_OF_MONTH);
int week = (Calendar.DAY_OF_WEEK);
(year+"年"+month+"月"+day+"日"+getWeek(week));
}
public static String getWeek(int i) {
String[] weeks = {"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
return weeks[i];
}
第八章:IO流
一、输入流和输出流
相对于内存设备而言
将外设(硬盘、U盘、打印机)中的数据读取到内存中:输入
将内存中的数据写入到外设(硬盘、U盘、打印机)中:输出。
二、字符流的由来:
其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。再对这个文字进行操作。简单说:字符流=字节流+编码表
三、编码表
美国的 ASCII码
中国的 GBK码
国际的 Unicode码(包含所有国家的编码,但是按自己的方式来编码,所以gbk中的“你”跟Unicode中的编码不一定相同)
四、FileWriter类--Writer的孙子类--OutputStreamWriter的子类
创建一个可以往文件中写入字符数据的字符输出流对象。
既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)。
如果文件不存在,则会自动创建。
如果文件存在,则会被覆盖。
如果构造函数中加入true,可以实现对文件进行续写!
/*
* 调用Writer对象中的write(string)方法,写入数据。
* 其实数据写入到临时存储缓冲区中。要();或()才会写入到文件中
*/
FileWriter fw = new FileWriter("F:\\",true);
("abcde"+("")+"hahaha");
();
五、FileReader类--Reader的孙子类--InputStreamReader的子类
读取文件中数据的两种方法:
/*
* 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。
*
* 用一个读取流关联一个已存在文件。
*/
1、使用read()方法读取字符,一次只能读取一个
FileReader fr = new FileReader("");
int ch = 0;
while((ch=())!=-1){
((char)ch);
}
();
2、使用read(char[])读取文本文件数据。一次读出一堆数据
char[] buf = new char[1024];
int len = 0;
while((len=(buf))!=-1){
(new String(buf,0,len));
}
六、面试题:从一个文件中读取出里面所有数据,然后写入另一个文件中。
FileReader fr = new FileReader("");
FileWriter fw = new FileWriter("");
char[] buf = new char[1024];
int len =0;
while((len = (buf))!=-1){
(new String(buf,0,len));
}
();
();
七、BufferedWriter类
//为了提高写入的效率。可以使用字符流的缓冲区。
//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
FileWriter fw = new FileWriter("");
BufferedWriter bufw = new BufferedWriter(fw);
//使用缓冲区的写入方法将数据先写入到缓冲区中。
("xixiixii");
();//换行
("heheheheh");
//使用缓冲区的刷新方法将数据刷目的地中。
//();
//关闭缓冲区。其实关闭的就是被缓冲的流对象。
();
八、BufferedReader类
FileReader fr = new FileReader("");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=())!=null){//readLine():返回文本文件中一行数据,没有则返回null
(line);
}
();
九、面试题:使用缓冲区从一个文件中读取出里面所有数据,然后写入另一个文件中。
FileReader fr = new FileReader("");
FileWriter fw = new FileWriter("");
BufferedReader bufr = new BufferedReader(fr);
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while((line=())!=null){
(line);
();
();
}
();
();
十、FileOutputStream、FileInputStream、BufferedOutputStream、BufferedInputStream类
练习:复制一个mp3
方法1:
public static void copy_1() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\1.mp3");
byte[] buf = new byte[1024];
int len = 0;
while((len=(buf))!=-1){
(buf,0,len);
}
();
();
}
方法2:已经用了缓冲流就不用再用缓冲区byte[] buf 了
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch=())!=-1){
(ch);
}
();
();
}
方法3: 不建议,() 把一个文件大小都读进来了,如果文件过大,内存会用完
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[()];
(buf);
(buf);
();
();
}
方法4:千万不要用,效率很差,读一个写一个!
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int ch = 0;
while((ch =())!=-1){
(ch);
}
();
();
}
十一、InputStreamReader类和OutputStreamWriter类(字节字符转换类)
十二、流的操作规律
1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer
2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3,明确具体的设备。
源设备:
硬盘:File
键盘:
内存:数组
网络:Socket流
目的设备:
硬盘:File
控制台:
内存:数组
网络:Socket流
4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("");
FileWriter fw = new FileWriter("");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader(""));
BufferedWriter bufw = new BufferedWriter(new FileWriter(""));
BufferedReader bufr = new BufferedReader(new FileReader(""));
BufferedWriter bufw = new BufferedWriter(new FileWriter(""));
FileReader fr = new FileReader("");
FileWriter fw = new FileWriter("");
BufferedReader bur = new BufferedReader(fr);
BufferedWriter buw = new BufferedWriter(fw);
String line = null;
while((line=())!=null){
(line);
();
();
}
();
();
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。
目的:
硬盘。File
InputStream in = ;
FileWriter fw = new FileWriter("");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader();
FileWriter fw = new FileWriter("");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader());
BufferedWriter bufw = new BufferedWriter(new FileWriter(""));
InputStream in = ;
FileWriter fw = new FileWriter("");
InputStreamReader isr = new InputStreamReader(in);
BufferedReader bur = new BufferedReader(isr);
BufferedWriter buw = new BufferedWriter(fw);
String line = null;
while((line=())!=null){
if(("over")){
break;
}
(line);
();
();
}
();
();
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:
FileReader fr = new FileReader("");
OutputStream out = ;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("");
OutputStreamWriter osw = new OutputStreamWriter();
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader(""));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter());
FileReader fr = new FileReader("");
OutputStream out = ;
OutputStreamWriter opsw = new OutputStreamWriter(out);
BufferedReader bur = new BufferedReader(fr);
BufferedWriter buw = new BufferedWriter(opsw);
String line = null;
while((line=())!=null){
(line);
();
();
}
();
();
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:
目的:
控制台:
InputStream in = ;
OutputStream out = ;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader();
OutputStreamWriter osw = new OutputStreamWriter();
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader());
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter());
InputStream in = ;
OutputStream out = ;
InputStreamReader isr = new InputStreamReader(in);
OutputStreamWriter opsw = new OutputStreamWriter(out);
BufferedReader bur = new BufferedReader(isr);
BufferedWriter buw = new BufferedWriter(opsw);
String line = null;
while((line=())!=null){
if("over".equals(line)){
break;
}
(line);
();
();
}
();
();
十三、转换流
什么时候使用转换流呢?
1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
练习:将一个中文字符串数据按照指定的编码表(utf-8)写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = new FileWriter("");
("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表(gbk)。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(""),charsetName);
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(""),charsetName));
写入:
OutputStreamWriter opsw = new OutputStreamWriter(new FileOutputStream(""),"utf-8");
("你好");
();
读取:
InputStreamReader ipsr = new InputStreamReader(new FileInputStream(""),"utf-8");
char[] buf = new char[10];
int len = (buf);
(new String(buf,0,len));
();
十四、File类
1、构造函数
//可以将一个已存在的,或者不存在的文件或者目录封装成file对象。
File f1 = new File("c:\\");
File f2 = new File("c:\\","");
File f = new File("c:\\");
File f3 = new File(f,"");
File f4 = new File("c:"++"abc"++"");
2、获取
获取文件名称:
String name = ();
获取文件路径:
String absPath =();//绝对路径;String path =();//相对路径
获取文件大小:
long len = ();
获取文件修改时间:
long time = ();
3、创建与删除:和输出流不一样,如果文件不存在,则创建,如果文件存在,则不创建。
();//创建文件
();//删除文件
();//创建目录
();//删除目录,如果目录中有内容,则无法删除
();//创建多级目录
4、判断
();判断文件是否存在
();判断是否文件
();判断是否目录
5、重命名
File f1 = new File("c:\\9.mp3");
File f2 = new File("d:\\aa.mp3");
boolean b = (f2);//发生剪切,c:\\9.mp3会剪切到d:\\aa.mp3
6、获取某一目录下的所有文件
File dir = new File("H:\\");
String[] names = ();
for(String name : names){
(name);
}
7、练习:获取某一目录下后缀名为“.txt”的所有文件名
File dir = new File("H:\\");
String[] names = (new FilterByName(".txt"));
for(String name : names){
(name);
}
public class FilterByName implements FilenameFilter {
private String houzhui;
FilterByName(String houzhui){
=houzhui;
}
public boolean accept(File dir, String name) {
return (houzhui);
}
}
十五、递归遍历某一目录下所有文件和目录
public static void main(String[] args) {
File dir = new File("H:\\digui");
digui(dir,0);
}
public static void digui(File dir,int level) {
(insSpace(level)+());
level++;
File[] files = ();
for(int x=0;x<;x++){
if(files[x].isDirectory()){
digui(files[x],level);
}else{
(insSpace(level)+files[x].getName());
}
}
}
public static String insSpace(int level) {
StringBuilder sb = new StringBuilder();
for(int x=0;x<level;x++){
("--");
}
return ();
}
十六、递归练习:删除一个带内容的目录。
public static void main(String[] args) {
File dir = new File("H:\\digui");
digui(dir);
}
public static void digui(File dir) {
File[] files = ();
for(int x=0;x<;x++){
if(files[x].isDirectory()){
digui(files[x]);
}else{
files[x].delete();
}
}
();
}
十七、Properties
* Properties集合:
* 特点:
* 1,该集合中的键和值都是字符串类型。
* 2,集合中的数据可以保存到流中,或者从流获取。
* 通常该集合用于操作以键值对形式存在的配置文件。
练习:对一个文件中的数据进行修改再存入
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("");
(fis);//装载
Set<String> keys = ();
for(String key : keys){
if(("lisi")){
//String value = (key);
(key, "200");
}
}
//();测试用的,输出所有键值对
FileOutputStream fos = new FileOutputStream("");
(fos, "updateInfo");//存入
();
();
十八、练习
* 获取指定目录下,指定扩展名的文件(包含子目录中的)
* 这些文件的绝对路径写入到一个文本文件中。
* 简单说,就是建立一个指定扩展名的文件的列表。
* 思路:
* 1,必须进行深度遍历。
* 2,要在遍历的过程中进行过滤。将符合条件的内容都存储到容器中。
* 3,对容器中的内容进行遍历并将绝对路径写入到文件中。
public class Test {
public static void main(String[] args) throws IOException {
File dir = new File("e:\\java0331");
FilenameFilter filter = new FilenameFilter(){
public boolean accept(File dir, String name) {
return (".java");
}
};
List<File> list = new ArrayList<File>();
getFiles(dir,filter,list);
File destFile = new File(dir,"");
write2File(list,destFile);
}
/**
* 对指定目录中的内容进行深度遍历,并按照指定过滤器,进行过滤,
* 将过滤后的内容存储到指定容器List中。
*/
public static void getFiles(File dir,FilenameFilter filter,List<File> list){
File[] files = ();
for(File file : files){
if(()){
//递归啦!
getFiles(file,filter,list);
}else{
//对遍历到的文件进行过滤器的过滤。将符合条件File对象,存储到List集合中。
if((dir, ())){
(file);
}
}
}
}
public static void write2File(List<File> list,File destFile)throws IOException{
BufferedWriter bufw = null;
try {
bufw = new BufferedWriter(new FileWriter(destFile));
for(File file : list){
(());
();
();
}
} /*catch(IOException e){
throw new RuntimeException("写入失败");
}*/finally{
if(bufw!=null)
try {
();
} catch (IOException e) {
throw new RuntimeException("关闭失败");
}
}
}
}
十九、打印流:PrintStream和PrintWriter
打印流:
PrintStream:字节打印流。
特点:
1,构造函数接收File对象,字符串路径,字节输出流。意味着打印目的可以有很多。
2,该对象具备特有的方法 打印方法 print println,可以打印任何类型的数据。
3,特有的print方法可以保持任意类型数据表现形式的原样性,将数据输出到目的地。
对于OutputStream父类中的write,是将数据的最低字节写出去。
PrintWriter:字符打印流。
特点:
1,当操作的数据是字符时,可以选择PrintWriter,比PrintStream要方便。
2,它的构造函数可以接收 File对象,字符串路径,字节输出流,字符输出流。
3,构造函数中,如果参数是输出流,那么可以通过指定另一个参数true完成自动刷新,该true对println方法有效。
* PrintStream:
* 1,提供了打印方法可以对多种数据类型值进行打印。并保持数据的表示形式。
* 2,它不抛IOException.
* 构造函数,接收三种类型的值:
* 1,字符串路径。
* 2,File对象。
* 3,字节输出流。
PrintStream out = new PrintStream("");
// int by = read();
// write(by);
// (610);//只写最低8位,
// (97);//将97先变成字符保持原样将数据打印到目的地。
();
/*
* PrintWriter:字符打印流。
* 构造函数参数:
* 1,字符串路径。
* 2,File对象。
* 3,字节输出流。
* 4,字符输出流。
*
*/
BufferedReader bufr = new BufferedReader(new InputStreamReader());
PrintWriter out = new PrintWriter(new FileWriter(""),true);//自动刷新缓冲区
String line = null;
while((line=())!=null){
if("over".equals(line))
break;
(());
// ();
}
();
();
二十、序列流:SequenceInputStream//用于将几个流合并成一个流
特点:
1,将多个字节读取流和并成一个读取流,将多个源合并成一个源,操作起来方便。
2,需要的枚举接口可以通过(collection);
Vector<FileInputStream> vectors = new Vector<FileInputStream>();
(new FileInputStream(""));
(new FileInputStream(""));
(new FileInputStream(""));
Enumeration<FileInputStream> elements = ();
SequenceInputStream sis = new SequenceInputStream(elements);//构造器接受的是枚举
FileOutputStream fos = new FileOutputStream("");
byte[] buf = new byte[1024];
int len = 0;
while((len=(buf))!=-1){
(buf,0,len);
}
();
();
二十一、ArrayList中的对象转换为枚举对象:调用工具类(al);方法
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
(new FileInputStream(""));
(new FileInputStream(""));
(new FileInputStream(""));
Enumeration<FileInputStream> en = (al);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("");
byte[] buf = new byte[1024];
int len = 0;
while((len=(buf))!=-1){
(buf,0,len);
}
();
();
二十二、文件切割
private static final int SIZE = 1024*1024;
public static void main(String[] args) throws IOException {
File file = new File("H:\\a.mp3");
splitFile(file);
}
public static void splitFile(File file) throws IOException{
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = null;
File dir = new File("H:\\partFile");
if(!()){
();
}
byte[] buf = new byte[SIZE];
int len = 0;
int count=0;
while((len = (buf))!=-1){
fos = new FileOutputStream(new File(dir,(count++)+".part"));
(buf, 0, len);
();
}
();
();
}
二十三、文件合并
private static final int SIZE = 1024*1024;
public static void main(String[] args) throws IOException {
File dir = new File("H:\\partFile");
mergeFile(dir);
}
private static void mergeFile(File dir) throws IOException {
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
String[] str = ();
for(int x=0;x<;x++){
(new FileInputStream(new File(dir,x+".part")));
}
Enumeration<FileInputStream> en = (al);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream(new File(dir,"a.mp3"));
byte[] buf = new byte[SIZE];
int len = 0;
while((len = (buf))!=-1){
(buf,0,len);
();
}
();
();
}
二十四、改进版的文件切割,合并
private static void splitFile_2(File file) throws IOException {
// 用读取流关联源文件。
FileInputStream fis = new FileInputStream(file);
// 定义一个1M的缓冲区。
byte[] buf = new byte[SIZE];
// 创建目的。
FileOutputStream fos = null;
int len = 0;
int count = 1;
/*
* 切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数。 以方便于合并。
* 这个信息为了进行描述,使用键值对的方式。用到了properties对象
*
*/
Properties prop = new Properties();
File dir = new File("c:\\partfiles");
if (!())
();
while ((len = (buf)) != -1) {
fos = new FileOutputStream(new File(dir, (count++) + ".part"));
(buf, 0, len);
();
}
//将被切割文件的信息保存到prop集合中。
("partcount", count+"");
("filename", ());
fos = new FileOutputStream(new File(dir,count+".properties"));
//将prop集合中的数据存储到文件中。
(fos, "save file info");
();
();
}
public static void mergeFile_2(File dir) throws IOException {
/*
* 获取指定目录下的配置文件对象。
*/
File[] files = (new SuffixFilter(".properties"));
if(!=1)
throw new RuntimeException(dir+",该目录下没有properties扩展名的文件或者不唯一");
//记录配置文件对象。
File confile = files[0];
//获取该文件中的信息================================================。
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(confile);
(fis);
String filename = ("filename");
int count = (("partcount"));
//获取该目录下的所有碎片文件。 ==============================================
File[] partFiles = (new SuffixFilter(".part"));
if(!=(count-1)){
throw new RuntimeException(" 碎片文件不符合要求,个数不对!应该"+count+"个");
}
//将碎片文件和流对象关联 并存储到集合中。
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=0; x<; x++){
(new FileInputStream(partFiles[x]));
}
//将多个流合并成一个序列流。
Enumeration<FileInputStream> en = (al);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream(new File(dir,filename));
byte[] buf = new byte[1024];
int len = 0;
while((len=(buf))!=-1){
(buf,0,len);
}
();
();
}
二十五、操作对象的流ObjectOutputStream和ObjectInputStream
ObjectOutputStream和ObjectInputStream
对象的序列化和反序列化。(反序列化只能对序列化了的对象进行)
writeObject readObject
public static void writeObj() throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(""));
//对象序列化。被序列化的对象必须实现Serializable接口。静态变量和瞬态不能写进硬盘,不能被序列化 ,即被static和transient修饰的变量
(new Person("小强",30));
();
}
public static void readObj() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(""));
//对象的反序列化。
Person p = (Person)();
(()+":"+());
();
}
/*
* Serializable:用于给被序列化的类加入ID号,用于判断类和对象是否是同一个版本。
* 该接口没有任何方法
*/
public class Person implements Serializable/*标记接口*/ {
/**
* transient:非静态数据不想被序列化可以使用这个关键字修饰。
*/
private static final long serialVersionUID = 9527l;//由于编译器的不同,所以建议显示版本号,这样即使不同的编译器的版本号都相同,即使把age的private改为public时版本号也相同,可以反序列化
private transient String name;
private static int age;
}
二十六、RandomAccessFile:不是io体系中的子类。
特点:
1,即可读取,又可以写入。
2,内部维护了一个大型的byte数组,通过对数组的操作完成读取和写入。
3,通过getFilePointer方法获取指针的位置,还可以通过seek方法设置指针的位置。
4,该对象的内容应该封装了字节输入流和字节输出流。
5,该对象只能操作文件。
通过seek方法操作指针,可以从这个数组中的任意位置上进行读和写
可以完成对数据的修改。
但是要注意:数据必须有规律。
public static void randomWrite() throws IOException{
RandomAccessFile raf = new RandomAccessFile("", "rw");
//往指定位置写入数据。
(3*8);
("哈哈".getBytes());
(108);
();
}
public static void readFile() throws IOException {
RandomAccessFile raf = new RandomAccessFile("", "r");
//通过seek设置指针的位置。
(1*8);//随机的读取。只要指定指针的位置即可。
byte[] buf = new byte[4];
(buf);
String name = new String(buf);
int age = ();
("name="+name);
("age="+age);
("pos:"+());
();
}
//使用RandomAccessFile对象写入一些人员信息,比如姓名和年龄。
public static void writeFile() throws IOException{
/*
* 如果文件不存在,则创建,如果文件存在,不创建
*/
RandomAccessFile raf = new RandomAccessFile("","rw");
("张三".getBytes());
(97);
("小强".getBytes());
(99);
();
}
二十七、管道流:需要和多线程技术相结合的流对象。(用的情况不多)
PipedOutputStream
PipedInputStream
public static void main(String[] args) throws IOException {
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
(output);
new Thread(new Input(input)).start();
new Thread(new Output(output)).start();
}
class Input implements Runnable{
private PipedInputStream in;
Input(PipedInputStream in){
= in;
}
public void run(){
try {
byte[] buf = new byte[1024];
int len = (buf);
String s = new String(buf,0,len);
("s="+s);
();
} catch (Exception e) {
// TODO: handle exception
}
}
}
class Output implements Runnable{
private PipedOutputStream out;
Output(PipedOutputStream out){
= out;
}
public void run(){
try {
(5000);
("hi,管道来了!".getBytes());
} catch (Exception e) {
// TODO: handle exception
}
}
}
二十八、用操作基本数据类型值的对象。
DataInputStream
DataOutputStream
public static void readData() throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream(""));
String str = ();
(str);
}
public static void writeData() throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(""));
("你好");//这是修改版的UTF-8,用转换流是读不出来的,只有用它自己的readUTF()方法才能读出来
();
}
二十九、设备是内存的流对象。
ByteArrayInputStream ByteArrayOutputStream
CharArrayReader CharArrayWriter
public static void main(String[] args) {
ByteArrayInputStream bis = new ByteArrayInputStream("abcedf".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch = 0;
while((ch=())!=-1){
(ch);
}
(());
}
三十、IO流体系:
字符流:
Reader
|--BufferedReader:
|--LineNumberReader
|--CharArrayReader
|--StringReader
|--InputStreamReaer
|--FileReader
Writer
|--BufferedWriter
|--CharArrayWriter
|--StringWriter
|--OutputStreamWriter
|--FileWriter
|--PrintWriter
字节流:
InputStream
|--FileInputStream:
|--FilterInputStream
|--BufferedInputStream
|--DataInputStream
|--ByteArrayInputStream
|--ObjectInputStream
|--SequenceInputStream
|--PipedInputStream
OutputStream
|--FileOutputStream
|--FilterOutputStream
|--BufferedOutputStream
|--DataOutputStream
|--ByteArrayOutputStream
|--ObjectOutputStream
|--PipedOutputStream
|--PrintStream
RandomAccessFile:
三十一、编码表
1、常见的编码表
2、编码转换
* 字符串 --> 字节数组:编码。
* 字节数组 --> 字符串:解码。
* 你好:GBK: -60 -29 -70 -61
* 你好: utf-8: -28 -67 -96 -27 -91 -67
* 如果你编错了,解不出来。
* 如果编对了,解错了,有可能有救。
3、解错了,有救的情况:(开始用ISO8859-1解错)
String str = "你好";
byte[] b= ();//默认是getBytes("gbk")编码 -》-60 -29 -70 -61
String str2 = new String(b,"ISO8859-1");//使用"ISO8859-1"解码解出乱码-》????(这几个问号不一样)
byte[] b2 = ("ISO8859-1");//使用"ISO8859-1"进行编码-》-60 -29 -70 -61
String str3 = new String(b2,"gbk");//使用gbk进行解码-》你好
(str3);
4、tomcat服务器中的编码转换
tomcat的服务器默认的是使用ISO8859-1来进行解码,假设项目是统一使用gbk(或utf-8)进行编码,浏览器向服务器端传送的是使用了gbk编码后的文字,tomcat服务器自动解码后会出现乱码 ???? ,这时要把乱码重新用ISO8859-1编码一次,然后在用gbk进行解码,就得到浏览器发送过来的文字了。
该方法是jsp开发中经常用到的乱码解决方法:
String name1 =new String(("name").getBytes("ISO8859_1"),"gbk");
("name")获取的是????,所以要重新编码解码一次
5、解错了,没有救的情况:(开始用utf-8解错)
String str = "你好";
byte[] b= ();//默认是getBytes("gbk")编码
String str2 = new String(b,"utf-8");//使用"utf-8"解码解出乱码
byte[] b2 = ("utf-8");//使用"utf-8"进行编码
String str3 = new String(b2,"gbk");//使用gbk进行解码-》解出来的还是乱码
(str3);
三十二:记事本中联通问题
在记事本中输入“联通”两字,保存后重新打开出现乱码。
因为记事本默认是按照ANSI,即本机编码gbk,而“联通”两字刚好被记事本当成utf-8来解码,所以出现错误。解决方法1:多写几个字;2:另存为utf-8编码,打开后就正常了。
三十三、练习:按字节截取字符串
public class Test {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
String str = "ab你好cd谢谢";
// str = "ab琲琲cd琲琲";
// int len = ("gbk").length;
// for(int x=0; x<len; x++){
// ("截取"+(x+1)+"个字节结果是:"+cutStringByByte(str, x+1));
// }
int len = ("utf-8").length;
for(int x=0; x<len; x++){
("截取"+(x+1)+"个字节结果是:"+cutStringByU8Byte(str, x+1));
}
// String str = "琲";
// byte[] buf = ("gbk");
// for(byte b : buf){
// (b);//-84 105
// }
}
/*
在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。
但对应的字节数不同,一个汉字占两个字节。
定义一个方法,按照最大的字节数来取子串。
如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,
那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”.
*/
public static String cutStringByU8Byte(String str, int len) throws IOException {
byte[] buf = ("utf-8");
int count = 0;
for(int x=len-1; x>=0; x--){
if(buf[x]<0)
count++;
else
break;
}
if(count%3==0)
return new String(buf,0,len,"utf-8");
else if(count%3==1)
return new String(buf,0,len-1,"utf-8");
else
return new String(buf,0,len-2,"utf-8");
}
public static String cutStringByByte(String str,int len) throws IOException{
byte[] buf = ("gbk");
int count = 0;
for(int x=len-1; x>=0; x--){
if(buf[x]<0)
count++;
else
break;
}
if(count%2==0)
return new String(buf,0,len,"gbk");
else
return new String(buf,0,len-1,"gbk");
}
}
第九章:网络编程
一、ip地址
InetAddress ip =("");//192.168.101.6
//根据主机名或IP地址获取IP地址对象
//= ();获取本机IP地址对象
(());//获取IP地址
(());//获取主机名
DNS域名解析:当我们访问localhost时,因为自己本机上配置了127.0.0.1,所以就先访问本机的,如果本机上没配置,就会去访问局域网或广域网上DNS服务器上的数据找到IP地址,然后返回给浏览器再接着访问服务器。
二、UDP传输
发送端:
public class UDPSendDemo {
public static void main(String[] args) throws IOException {
("发送端启动......");
/*
* 创建UDP传输的发送端。
* 思路:
* 1,建立udp的socket服务。
* 2,将要发送的数据封装到数据包中。
* 3,通过udp的socket服务将数据包发送出去。
* 4,关闭socket服务。
*/
//1,udpsocket服务。使用DatagramSocket对象。
DatagramSocket ds = new DatagramSocket(8888);
//2,将要发送的数据封装到数据包中。
String str = "udp传输演示:哥们来了!";
//使用DatagramPacket将数据封装到的该对象包中。
byte[] buf = ();
DatagramPacket dp =
new DatagramPacket(buf,,("192.168.101.6"),10000);
//3,通过udp的socket服务将数据包发送出去。使用send方法。
(dp);
//4,关闭资源。
();
}
}
接收端:
public class UDPReceDemo {
public static void main(String[] args) throws IOException {
("接收端启动......");
/*
* 建立UDP接收端的思路。
* 1,建立udp socket服务,因为是要接收数据,必须要明确一个端口号。
* 2,创建数据包用于存储接收到的数据。方便用数据包对象的方法解析这些数据.
* 3,使用socket服务的receive方法将接收的数据存储到数据包中。
* 4,通过数据包的方法解析数据包中的数据。
* 5,关闭资源
*/
//1,建立udp socket服务。
DatagramSocket ds = new DatagramSocket(10000);
//2,创建数据包。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,);
//3,使用接收方法将数据存储到数据包中。
(dp);//阻塞式的。
//4,通过数据包对象的方法,解析其中的数据,比如,地址,端口,数据内容。
String ip = ().getHostAddress();
int port = ();
String text = new String((),0,());
(ip+":"+port+":"+text);
//5,关闭资源。
();
}
}
三、聊天程序
public class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds){
= ds;
}
public void run() {
try {
BufferedReader bufr = new BufferedReader(new InputStreamReader());
String line = null;
while((line=())!=null){
byte[] buf = ();
DatagramPacket dp = new DatagramPacket
(buf,,("192.168.101.255"),10001);
//这里使用255广播IP,发给该IP后192.168.101网段的IP都能收到
(dp);
if("886".equals(line))
break;
}
();
} catch (Exception e) {
}
}
}
public class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
= ds;
}
public void run() {
try {
while (true) {
// 2,创建数据包。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, );
// 3,使用接收方法将数据存储到数据包中。
(dp);// 阻塞式的。
// 4,通过数据包对象的方法解析其中的数据,如地址,端口,数据内容。
String ip = ().getHostAddress();
int port = ();
String text = new String((), 0, ());
(ip + "::" + text);
if(("886")){
(ip+"....退出聊天室");
}
}
} catch (Exception e) {
}
}
}
public class ChatDemo {
public static void main(String[] args) throws IOException {
DatagramSocket send = new DatagramSocket();
DatagramSocket rece = new DatagramSocket(10001);
new Thread(new Send(send)).start();
new Thread(new Rece(rece)).start();
}
}
四、TCP传输
客户端:
public class TCPClient {
public static void main(String[] args) throws IOException, ClassNotFoundException{
//客户端发数据到服务端
/*
* Tcp传输,客户端建立的过程。
* 1,创建tcp客户端socket服务。使用的是Socket对象。
* 建议该对象一创建就明确目的地。要连接的主机。
* 2,如果连接建立成功,说明数据传输通道已建立。
* 该通道就是socket流 ,是底层建立好的。 既然是流,说明这里既有输入, 又有输出。
* 想要输入或者输出流对象,可以找Socket来获取。
* 可以通过getOutputStream(),和getInputStream()来获取两个字节流。
* 3,使用输出流,将数据写出。
* 4,关闭资源。
*/
Socket s = new Socket("192.168.101.6",10002);
OutputStream out = ();
("TCP演示:哥们来了".getBytes());
//读取服务端返回的数据,使用socket读取流。
InputStream in = ();
byte[] buf = new byte[1024];
int len = (buf);
String text = new String(buf,0,len);
(text);
();
}
}
服务端:
public class TCPServer {
public static void main(String[] args) throws IOException, ClassNotFoundException{
/*
* 建立tcp服务端的思路:
* 1,创建服务端socket服务。通过ServerSocket对象。
* 2,服务端必须对外提供一个端口,否则客户端无法连接。
* 3,获取连接过来的客户端对象。
* 4,通过客户端对象获取socket流读取客户端发来的数据,并打印在控制台上。
* 5,关闭资源。关客户端,关服务端。
*/
ServerSocket ss = new ServerSocket(10002);
Socket s = ();
InputStream in = ();
String ip = ().getHostAddress();
byte[] buf = new byte[1024];
int len = (buf);
String text = new String(buf,0,len);
(ip+"--"+text);
//使用客户端socket对象的输出流给客户端返回数据
OutputStream out = ();
("收到".getBytes());
();
();
}
}
五、字母大小写传输转换
客户端:
public class Client {
public static void main(String[] args) throws IOException, IOException {
Socket s = new Socket("192.168.101.6",10004);
BufferedReader br = new BufferedReader(new InputStreamReader());
//PrintWriter pw = new PrintWriter((),true);
BufferedWriter bw = new BufferedWriter
(new OutputStreamWriter(()));
//读取服务端返回的大写数据
BufferedReader brIn = new BufferedReader
(new InputStreamReader(()));
String line = null;
while((line=())!=null){
if("over".equals(line))
break;
(line);
();
();
//读取服务端发送过来的数据并显示在控制台上
(());
}
();
}
}
服务端:
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10004);
Socket s = ();
String ip = ().getHostAddress();
BufferedReader br = new BufferedReader
(new InputStreamReader(()));
BufferedWriter bw = new BufferedWriter
(new OutputStreamWriter(()));
String line = null;
while((line=())!=null){
(ip+"--"+line);
(());
();
();
}
();
();
}
}
六、上传文本文件
客户端:
public class Client2 {
public static void main(String[] args) throws IOException, IOException {
Socket s = new Socket("192.168.101.6",10005);
BufferedReader br = new BufferedReader(new FileReader("H:\\"));
BufferedWriter bw = new BufferedWriter
(new OutputStreamWriter(()));
String line = null;
while((line=())!=null){
(line);
();
();
}
();//告诉服务端,客户端写完了。
BufferedReader bufr = new BufferedReader
(new InputStreamReader(()));
(());
();
();
}
}
服务端:
public class Server2 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10005);
Socket s = ();
BufferedReader br = new BufferedReader
(new InputStreamReader(()));
BufferedWriter bw = new BufferedWriter(new FileWriter("H:\\"));
String line = null;
while((line=())!=null){
(line);
();
();
}
BufferedWriter bufw = new BufferedWriter
(new OutputStreamWriter(()));
("上传成功");
();
();
();
();
}
}
七、上传非文本文件
客户端:
public class Client3 {
public static void main(String[] args) throws IOException, IOException {
Socket s = new Socket("192.168.101.6",10006);
BufferedInputStream bis = new BufferedInputStream
(new FileInputStream("H:\\pic\\client.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(());
int len = 0;
byte[] buf = new byte[1024];
while((len=(buf))!=-1){
(buf, 0, len);
();
}
();
BufferedInputStream bufIs = new BufferedInputStream(());
byte[] bufIn = new byte[1024];
int lenIn = (bufIn);
(new String(bufIn,0,lenIn));
();
();
}
}
服务端:
public class Server3 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10006);
Socket s = ();
String ip = ().getHostAddress();
File dir = new File("H:\\pic");
if(!()){
();
}
BufferedInputStream bis = new BufferedInputStream(());
BufferedOutputStream bos = new BufferedOutputStream
(new FileOutputStream(new File(dir,ip+".mp3")));
byte[] buf = new byte[1024];
int len = 0;
while((len=(buf))!=-1){
(buf);
();
}
BufferedOutputStream bufos = new BufferedOutputStream(());
("上传成功".getBytes());
();
();
();
();
}
}
八、服务器端多线程技术
public class UploadPicClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1,创建客户端socket。
Socket s = new Socket("192.168.1.100",10006);
//2,读取客户端要上传的图片文件。
FileInputStream fis = new FileInputStream("c:\\");
//3,获取socket输出流,将读到图片数据发送给服务端。
OutputStream out = ();
byte[] buf = new byte[1024];
int len = 0;
while((len=(buf))!=-1){
(buf,0,len);
}
//告诉服务端说:这边的数据发送完毕。让服务端停止读取。
();
//读取服务端发回的内容。
InputStream in = ();
byte[] bufIn = new byte[1024];
int lenIn = (buf);
String text = new String(buf,0,lenIn);
(text);
();
();
}
}
public class UploadPicServer {
public static void main(String[] args) throws IOException {
//创建tcp的socket服务端。
ServerSocket ss = new ServerSocket(10006);
while(true){
Socket s = ();
new Thread(new UploadTask(s)).start();
}
}
}
public class UploadTask implements Runnable {
private static final int SIZE = 1024*1024*2;
private Socket s;
public UploadTask(Socket s) {
= s;
}
public void run() {
int count = 0;
String ip = ().getHostAddress();
(ip + ".....connected");
try{
InputStream in = ();
File dir = new File("c:\\pic");
if (!()) {
();
}
File file = new File(dir, ip + ".jpg");
//如果文件已经存在于服务端
while(()){
file = new File(dir,ip+"("+(++count)+").jpg");
}
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = (buf)) != -1) {
(buf, 0, len);
if(()>SIZE){
(ip+"文件体积过大");
();
();
(ip+"...."+());
return ;
}
}
// 获取socket输出流,将上传成功字样发给客户端。
OutputStream out = ();
("上传成功".getBytes());
();
();
}catch(IOException e){
}
}
}
九、我的tomcat服务器
public class MyTomcat {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket s = ();
(().getHostAddress()+".....connected");
InputStream in = ();
byte[] buf = new byte[1024];
int len = (buf);
String text = new String(buf,0,len);
(text);
//给客户端一个反馈信息。
PrintWriter out = new PrintWriter((),true);
("<font color='red' size='7'>欢迎光临</font>");
();
();
}
}
十、我的浏览器
public class MyBrowser {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket s = new Socket("192.168.1.100",8080);
//模拟浏览器,给tomcat服务端发送符合http协议的请求消息。
PrintWriter out = new PrintWriter((),true);
("GET /myweb/ HTTP/1.1");
("Accept: */*");
("Host: 192.168.1.100:8080");
("Connection: close");
();
();
InputStream in = ();
byte[] buf = new byte[1024];
int len = (buf);
String str =new String(buf,0,len);
(str);
();
//http://192.168.1.100:8080/myweb/
}
}
public class URLDemo {
public static void main(String[] args) throws IOException {
String str_url = "http://192.168.1.100:8080/myweb/";
URL url = new URL(str_url);
// InputStream in = ();//拆成下面两个方法
//获取url对象的Url连接器对象。将连接封装成了对象:java中内置的可以解析的具体协议的对象+socket.
URLConnection conn = ();
// String value = ("Content-Type");
// (value);
// (conn); //:http://192.168.1.100:8080/myweb/
InputStream in = ();
byte[] buf = new byte[1024];
int len = (buf);
String text = new String(buf,0,len);
(text);
();
}
}
十一、java反射机制
* JAVA反射机制是在运行状态中,对于任意一个类 (class文件),都能够知道这个类的所有属性和方法;
* 对于任意一个对象,都能够调用它的任意一个方法和属性;
* 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
* 动态获取类中信息,就是java反射 。
* 可以理解为对类的解剖。
对于tomcat已经写好的应用程序,如果想要扩展其功能,先写好一个类实现她提供的接口,然后写入xml配置文件中,tomcat会自动去读取里面的配置文件信息然后根据里面提供的类名自动加载该类。
* 要想要对字节码文件进行解剖,必须要有字节码文件对象.
* 如何获取字节码文件对象呢?
获取字节码文件的三种方式:
1、通过Object对象的getClass()方法
Person p = new Person();
Class clazz = ();
2、通过任何数据类型都具备的一个静态属性.class来获取其对应的Class对象。
Class clazz = ;
3、通过给定的类的字符串名称获取,用Class类中的forName()方法完成。反射技术通常使用这种方法。
Class clazz = ("");
十二、获取Class中的构造函数来创建对象
1、有无参构造函数的对象的创建
Class clazz = ("");
Object obj = ();
2、没有无参构造函数的对象的创建
Class clazz = ("");
Constructor cs = (,);
Object obj = ("小强",20);
十三、获取Class中的字段
Class clazz = ("");
Field field = ("age");
(true);//对私有字段的访问取消权限检查。暴力访问。
Object obj = ();
Object o = (obj);
(o);
(obj, 12);
Object oo = (obj);
(oo);
十四、获取Class中的方法
获取空参数方法:
Class clazz = ("");
Method method = ("show", null);
Object obj = ();
//Constructor constructor = (,);
//Object obj = ("小明",37);
(obj, null);
获取有参数方法:
Class clazz = ("");
Method method = ("paramMethod", ,);
Object obj = ();
(obj, "李四",23);
十五、反射技术练习
电脑运行:
文件:
pci1=
pci2=
public class ReflectTest {
public static void main(String[] args) throws Exception {
Mainboard mb = new Mainboard();
();
//每次添加一个设备都需要修改代码传递一个新创建的对象
//(new SoundCard());
//能不能不修改代码就可以完成这个动作。
//不用new来完成,而是只获取其class文件。在内部实现创建对象的动作。
File configFile = new File("");
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(configFile);
(fis);
for(int x=0; x<(); x++){
String pciName = ("pci"+(x+1));
Class clazz = (pciName);//用Class去加载这个pci子类。
PCI p = (PCI)();
(p);
}
();
}
}
public class Mainboard {
public void run() {
("main board run....");
}
public void usePCI(PCI p) {
if (p != null) {
();
();
}
}
}
public interface PCI {
public void open();
public void close();
}
public class SoundCard implements PCI {
public void open(){
("sound open");
}
public void close(){
("sound close");
}
}
public class NetCard implements PCI {
public void open() {
("net open");
}
public void close() {
("net close");
}
}
十六、正则表达式
1、数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
* 正则表达式对字符串的常见操作:
* 1, 匹配。
* 其实使用的就是String类中的matches方法。
//匹配手机号码是否正确。
String str = "13829031111";
boolean b = ("1[358]\\d{9}");//1[358][0-9]{9}
(b);
* 2,切割。
* 其实使用的就是String类中的split方法。
String str = "zhangsan xiaoqiang zhaoliu";
String[] names = (" +");//根据一个或多个空格来拆分字符串
//String str = "zhangsan...xiaoqiang..zhaoliu";
//String[] names = ("\\.+");//根据一个或多个点来拆分字符串
// "."代表任意字符,(.)把它封装为组,编号为1...n,调用它直接用编号,1由于代 表的是正常的 1 ,用\1将它转义为组编号,由于是在字符串中,再用\\1来转义
//组:((A)(B(C))) 从左括号看,第1组是(A)(B(C)),第2组是A,第3组是B(C),第4组是C
String str = "zhangsanttttxiaoqiangmmmzhaoliu";
String[] names = ("(.)\\1+");
for(String name : names){
(name);
}
* 3,替换。
* 其实使用的就是String类中的replaceAll()方法。
String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";
str = ("(.)\\1+", "$1");//$符号调用了前面参数中的第1组
(str);zhangsantxiaoqiangmzhaoliu
String tel = "13829031111";
tel = ("(\\d{4})\\d{3}(\\d{4})", "$1****$2");
(tel);//1382****1111
* 4,获取。
* 将正则规则进行对象的封装。
* Pattern p = ("a*b");
* //通过正则对象的matcher方法字符串相关联。获取要对字符串操作的匹配器对象Matcher .
* Matcher m = ("aaaaab");
* //通过Matcher匹配器对象的方法对字符串进行操作。
* boolean b = ();
String str = "da jia hao,ming tian bu fang jia!";
String regex = "\\b[a-z]{3}\\b";
//1,将正则封装成对象。
Pattern p = (regex);
//2, 通过正则对象获取匹配器对象。
Matcher m = (str);
//使用Matcher对象的方法对字符串进行操作。
//既然要获取三个字母组成的单词
//查找。 find();
(str);
while(()){
(());//获取匹配的子序列
(()+":"+());
}
5、练习
治疗口吃:
String str = "我我...我我...我我我要...要要要要...要要要要..学学学学学...学学编编...编编编编..编..程程...程程...程程程";
str = ("\\.+", "").replaceAll("(.)\\1+", "$1");
(str);//我要学编程
对IP地址排序:
String ip_str = "192.168.10.34 127.0.0.1 3.3.3.3 105.70.11.55";
//1,为了让ip可以按照字符串顺序比较,只要让ip的每一段的位数相同。
//所以,补零,按照每一位所需做多0进行补充。每一段都加两个0.
ip_str = ip_str.replaceAll("(\\d+)", "00$1");
(ip_str);
//然后每一段保留数字3位。
ip_str = ip_str.replaceAll("0*(\\d{3})", "$1");
(ip_str);
//1,将ip地址切出。
String[] ips = ip_str.split(" +");
TreeSet<String> ts = new TreeSet<String>();
for(String ip : ips){
(ip);
}
for(String ip : ts){
(("0*(\\d+)", "$1"));
}
}
网页爬虫:
/*
* 网页爬虫:其实就一个程序用于在互联网中获取符合指定规则的数据。
*
* 爬取邮箱地址。
*
*/
public class RegexTest2 {
public static void main(String[] args) throws IOException {
List<String> list = getMailsByWeb();
for(String mail : list){
(mail);
}
}
public static List<String> getMailsByWeb() throws IOException {
//1,读取源文件。
// BufferedReader bufr = new BufferedReader(new FileReader("c:\\"));
URL url = new URL("http://192.168.1.100:8080/myweb/");
BufferedReader bufIn = new BufferedReader(new InputStreamReader(()));
//2,对读取的数据进行规则的匹配。从中获取符合规则的数据.
String mail_regex = "\\w+@\\w+(\\.\\w+)+";
List<String> list = new ArrayList<String>();
Pattern p = (mail_regex);
String line = null;
while((line=())!=null){
Matcher m = (line);
while(()){
//3,将符合规则的数据存储到集合中。
(());
}
}
return list;
}
public static List<String> getMails() throws IOException{
//1,读取源文件。
BufferedReader bufr = new BufferedReader(new FileReader("c:\\"));
//2,对读取的数据进行规则的匹配。从中获取符合规则的数据.
String mail_regex = "\\w+@\\w+(\\.\\w+)+";
List<String> list = new ArrayList<String>();
Pattern p = (mail_regex);
String line = null;
while((line=())!=null){
Matcher m = (line);
while(()){
//3,将符合规则的数据存储到集合中。
(());
}
}
return list;
}
}