标识符
Java 的标识符是由字母、数字、下划线_、以及美元符$组成,但是首字母不可以是数字。Java 标识符大小写敏感,长度无限制,不能是 Java 中的关键字。命名规则:要见名知意!
u 变量要首字母小写和驼峰原则;
u 常量的单词字母要全部大写,若有两个以上的单词组成,就用下划线"_"进行连接;
u 类名要首字母大写和驼峰原则;
u 方法名要首字母小写和驼峰原则,如showRun()
变量
通过变量来操纵存储空间的数据,变量就指代这个存储空间。空间的位置是确定的,但是里面存放的值不确定。
局部变量 | 存在于栈内存的方法中,生命周期伴随所属区域始终,使用前必须先声明和初始化 |
成员变量 (实例变量) | 存在于堆内存的对象中,从属对象,生命周期伴随对象始终,自动初始化、有默认初始值 |
静态变量 (类变量) | 存在于方法区,用static修饰,从属类,生命周期伴随类始终,自动初始化、有默认初始值 |
数据类型
1.分类
基本数据类型:逻辑型、文本型(char)、数值型
byte(1字节)、short(2)、int(4)、long(8)、float(4)、double(8)
引用数据类型:类、接口、数组
大小统一是4字节,记录的是其引用对象的地址!
注意:
u 实际上还有一种基本数据类型:void,其对应的包装类是:java.lang.Void
u Java 中迚行方法调用传递参数时,基本数据类型传递的是该数据值本身,引用数据类型传递的是对对象的引用——地址,而不是对象本身;Java 中只有值传递!
u 转义字符:\n换行 \r回车 \f换页 \’引号 \\反斜杠号
2.基本数据类型对象包装类
基本数据类型都有自己对应的包装类:byte Byte;short Short;int Integer;long Long;boolean Boolean;float Float;double Double;char Character
基本数据类型存储在栈内存中,而它们对应包装类的实例对象存储在栈内存中。
基本数据类型对象包装类的最常见作用,就是基本数据类型和字符串类型之间做转换
(1)基本数据类型转成字符串:基本数据类型+""或基本数据类型.toString(基本数据类型值);
Integer.toString(34);//将34整数变成"34";
(2)字符串转成基本数据类型:xxx a = Xxx.parseXxx(String);
int a =Integer.parseInt("123");
double b = Double.parseDouble("12.23");
boolean b =Boolean.parseBoolean("true");
//或者可以写成
Integer i = newInteger("123"); int num =i.intValue();
还可以进行进制之间的互换:
(1)十进制转成其他进制:toBinaryString();二 toOctalString();八 toHexString();十六
(2)其他进制转成十进制:arseInt(string,radix);//其中,radix=2,8,16
JDK1.5以后,简化了定义方式:Integer x = new Integer(4);可以直接写成
Integer x = 4;//自动装箱 x = x + 5;//自动拆箱,通过intValue方法。
注意:
u 使用时,如果写成Integerx = null;上面的代码就会出现NullPointerException;
u 定义对象时,当数值在byte范围内容,如果该数值已经存在,则不会在开辟新的空间;
u 对于数值型的取值范围,它们都以常量的形式义在对应的包装类中了。
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE="+ Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE="+ Byte.MAX_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="+(int) Character.MIN_VALUE);
System.out.println("最大值:Character.MAX_VALUE="+(int) Character.MAX_VALUE);
数组
数组是相同类型数据的集合。它的长度固定,一旦创建完成后长度是不可改变的。Java允许创建任何类型的数组,而数组的大小是int型变量,其值是非负数。
1.数组的内存结构
2.赋值与复制
(1)赋值(必须是类型相同的):可以让多个数组变量指向同一个数组。
int[ ]arrs=arr;
(2)复制:通过clone()方法,其实新建了一个数组
int[ ] cloneArr=(int[ ])arr.clone( );
注意:当数组类型是类(非基本数据类型)时,调用clone( )方法,复制的是数组的内容即引用,并没有复制引用指向的对象。
3.几种常见操作
(1)排序
选择排序法:从头角标对应的元素开始,和每个元素进行一次比较。第一次内循环后,最值出现在头角标位置
for (int x=0;x<arr.length-1 ; x++){
for(int y=x+1; y<arr.length; y++){
if(arr[x]>arr[y]){
swap(arr,x,y);}}}
冒泡排序:相邻两个元素以此比较,符合条件就调换顺序。第一次内循环后,最值出现在未角标位置
for(int x=0;x<arr.length-1; x++){
for(int y=0; y<arr.length-x-1; y++){//-x:让每一次比较的元素减少,-1:避免角标越界。
if(arr[y]<arr[y+1]){
swap(arr,y,y+1);}}}
(2)折半查找
public static inthalfSearch (int[] arr,int key){
int min = 0,max =arr.length-1,mid;
while(min<=max){
mid = (max+min)/2;
if(key>arr[mid])
min = mid + 1;
else if(key<arr[mid])
max = mid - 1;
else
return mid;
}
return -1;}}// 如果要想实现在一个有序的数组里面加入一个元素,要求加入后数组里面的元素任然有有序的。此处应该改成return min
4.二维数组
格式1:int[][] arr = new int[3][2]
l 定义了名称为arr的二维数组
l 二维数组中有3个一维数组,每一个一维数组中有2个元素
l 一维数组的名称分别为arr[0],arr[1], arr[2]
l 给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;
格式2:int[][] arr = new int[3][];
l 二维数组中有3个一维数组,每个一维数组都是默认初始化值null
l 可以对这个三个一维数组分别进行初始化:
arr[0] = new int[3];
arr[1] = new int[1];
arr[2] = new int[2];
格式3:int[][] arr = {{3,8,2},{2,7},{9,0,1,6}};
l 二维数组中的有三个一维数组,每一个一维数组中具体元素也都已初始化
第一个一维数组arr[0] = {3,8,2};
第二个一维数组arr[1] = {2,7};
第三个一维数组arr[2] = {9,0,1,6};
l 第三个一维数组的长度表示方式:arr[2].length;
多维数组:由一级级的一维数组组成,是数组的数组。除了最后一层数组由数组元素组成,其余的数组都是数组变量的数组。
内存结构
通过new建立的数组和对象的实例都存放在堆内存中。
每一个实体都有内存地址值
n 实体中的变量都有默认初始化值
n 实体不在被使用,会在不确定的时间内被垃圾回收器回收
键盘输入方法
1.接收一个字符:System.in.read();
char c=(char)System.in.read();
2.接收一个字符串:BufferedRead类和InputStreamReader类
BufferedReaderbr=new BufferedReader(new InputStreamReader(System.in));
Stringc=br.readLine();
3.接受任意数据:Scanner类
Scanner s=new Scanner(System.in); System.out.println("请输入你的姓名:"); String name=s.nextLine(); System.out.println("请输入你的年龄:"); int age=s.nextInt(); System.out.println("请输入你的工资:"); float salary=s.nextFloat(); | Scanner s=new Scanner(System.in); System.out.println("请输入你的年龄:"); int age=s.nextInt(); System.out.println("请输入你的姓名:"); String name=s.next(); System.out.println("请输入你的工资:"); float salary=s.nextFloat(); |
next()和nextLine()的区别:
在java中,next()方法是不接收空格的,在接收到有效数据前,所有的空格或者tab键等输入被忽略,若有有效数据,则遇到这些键退出。nextLine()可以接收空格或者tab键,其输入应该以enter键结束。
访问控制符
使用访问权限符修饰方法/成员变量的可见性
访问控制符 | 本类 | 同一个包中的类 | 子类 | 不同包中的类 |
public | √ | √ | √ | √ |
protected | √ | √ | √ | ╳ |
默认 | √ | √ | ╳ | ╳ |
private | √ | ╳ | ╳ | ╳ |
并不是绝对的,如果同时满足两个条件,要以前面的为主要标准。例如,对于A类中用protected修饰的方法test,异包中的B类继承A类,此时可以看成B是A的子类。对于默认访问权限符修饰的类,如果子类和父类在同一个包中,那么它对子类仍然可见。
跳转语句
break | 结束其所在的循环而不进入下一轮循环;如果有循环嵌套则不会终止外部循环 |
continue | 结束当次循环,继续进行下一轮循环 |
return | 返回数据,终止运行 |
面向对象
面向对象的特征:封装、继承、多态
1.构造函数
给对象进行初始化时,对象一建立就会调用与之对应的构造函数。当分析事物时,该事物具备一些特性或者行为,就将这些内容定义在构造函数中。
u 名称必须与类名一致,不能有返回值类型,没有具体的返回值,通过new调用。
u 系统会默认给该类加入一个空参数的构造函数。
u 可以利用重载的形式定义多个构造函数以对不同对象进行针对性的初始化。
构造代码块:给对象进行初始化,对象一建立就运行,而且优先于构造函数执行。定义的是不同对象共性的初始化内容,而构造函数是给对应的对象初始化。
把共性的初始化内容放在构造代码块中! {共性的内容;}
2.this关键字
代表它所在函数所属对象的引用。简单的说,哪个对象在调用this所在的函数,this就代表哪个对象。
super与this的用法几乎一致,this是本类对象的引用,super是父类对象的引用。
(1)this的用法:当定义类中功能(函数)时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。但凡本类功能内部使用了本类对象,都用this表示。
class Person{
private String name;
private int age;
Person(int age){
this.age = age;}
//需求:给人定义一个用于比较年龄是否相同的功能。也就是是否是同龄人。
public boolean compare(Person p){
return this.age==p.age; }
}
(2)this语句:用于构造函数之间进行互相调用。
u this语句只能定义在构造函数的第一行!因为初始化要先执行。
u 构造函数第一行,默认有一条隐式语句this()
class Person{
private String name;
private int age;
{
System.out.println("coderun");//构造代码块
}
Person(String name){
this();
this.name =name;}
Person(String name,int age){
this(name);
this.name = name;
this.age = age;}
}
3.static关键字
是一个修饰符,用于修饰成员(成员变量,成员函数),把对象的共享数据存储在单独的空间。当成员被静态修饰后,就有两种调用方式:被对象调用和直接被类名调用。
(1)特点:
u 随着类的加载而加载,随着类的消失而消失。说明它的生命周期最长。
u 优先于的对象存在。明确一点:静态是先存在。对象是后存在的。
u 修饰后被所有对象所共享。
u 可以直接被类名所调用。
(2)注意:
u 主函数是静态的。
u 静态方法中不可以定义this,super关键字。因为静态优先于对象存在。
u 静态方法只能访问静态成员。非静态方法既可以访问静态也可以访问非静态。
(3)什么时候使用静态:
什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰存在于方法区,对象中的特有数据要定义成非静态存在于堆内存中。
什么时候定义静态函数呢?
当功能内部没有访问到非静态数据(对象的特有数据),该功能可以定义成静态的。
继承
1.继承
Java只支持单继承,不支持多继承。但支持多层继承,就是一个继承体系。
子父类加载时,类成员的特点:
(1)成员变量
当子类中出现非私有的同名成员变量时,如果子类要访问本类中的变量,用this;要访问父类中的同名变量,用super。
(2)成员函数
当子类出现和父类一模一样的函数时,如果子类对象调用该函数,会运行子类函数的内容,这叫覆盖(或者重写)
u 子类覆盖父类,必须保证子类权限大于等于父类权限,否则编译失败。
u 静态只能覆盖静态。
(3)构造函数
当子类对象进行初始化时,父类的构造函数也会运行,因为子类所有的构造函数第一行默认有一条隐式的语句 super(),它会访问父类中空参数的构造函数。
u 当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
u 子类的构造函数第一行也可手动指定this语句来访问本类中的构造函数,再通过本类指定的构造函数来访问父类的构造函数
u 子类中至少会有一个构造函数会访问父类中的构造函数。
u 子类构造函数调用父类构造函数用:super(参数) super(name);
u 子类成员函数调用父类成员函数用:super.父类方法 super.show();
2.abstract关键字
抽象。当多个类中出现相同功能定义不同功能主体时,可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体。
(1)抽象类:
u 抽象方法和抽象类都必须被abstract关键字修饰,抽象方法一定在抽象类中。
u 抽象类不可以用new创建对象。抽象类中的抽象方法要被使用,必须由子类复写全部的抽象方法后才能建立子类对象进行调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。因为该子类继承了抽象类的抽象方法。
u 抽象类只是比一般类多了抽象方法,它可定义非抽象方法,也可不定义抽象方法。
u 需要指出的是,抽象类也是有构造函数的。
(2)abstract关键字不能和哪些关键字共存?
final:被final修饰后,不能被子类继承,无法重写。
private: 私有后,不能被子类继承,无法重写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。
3.final关键字
final作为一个修饰符,可以修饰变量,方法(函数),类
(1)修饰变量:是一个常量只能赋值一次,既可以修饰成员变量,也可以修饰局部变量
(2)修饰方法:不可以被子类重写但是可以重载
(3)修饰类:不能被继承
4.接口
当抽象类中的方法都是抽象方法时,该类可以通过接口的形式来表示。它是对外暴露的规则,是对程序的扩展。用interface定义,用implement实现。
(1)接口定义时,格式特点:
u 接口中常见定义:常量,抽象方法。
u 接口中的成员都有固定修饰符。
常量:publicstatic final
抽象方法:publicabstract
记住:接口中的成员都是public的,以上固定的格式可以省略,java虚拟机默认添加。
(2)接口是不可以创建对象的,因为有抽象方法,需要被子类实现。子类对接口中的抽象方法全都覆盖后,子类才可以实例化。
(3)区分关系:
类与接口之间是实现关系,并且可以多实现
接口与接口之间是继承关系,并且可以是多继承
多态
1.定义
(1)体现:父类引用指向子类对象
Animal a =new Cat();//左边是父类引用,右边是子类对象
(2)必要条件:类之间继承/实现的关系+重写+父类/接口的引用指向子类对象
(3)多态提高了程序的扩展性,但是父类的引用只能访问父类中的成员。可以通过强制转换来访问子类成员:
Animala=new Cat(); //向上转型
Catc=(Cat)a;//向下转型,强制转换
可以看出,多态自始至终都是子类的对象在改变
2.多态中成员的特点:
(1)成员函数:成员函数在多态调用时,编译看左边,运行看右边。
在编译时期:看引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:看对象所属的类中是否有调用的方法。
(2)成员变量和静态成员函数:无论编译还是运行,都看左边(引用型变量所属的类)。
3.instanceof运算符:返回值是boolean类型,判断一个引用指向的对象所属的类是不是某个类或者某个类的子类。左边是一个对象,右边是一个类
用法:对象的引用 instanceof 类名;
4.代码范例:主机接口
思想:先定义一个接口,让需要用到的类实现去这个接口。然后再定义一个类去使用这个接口,此时方法的参数就是接口。那么就可以定义接口的对象来使用重写后的方法。
引申:可以将这两个有相同方法的类A和B进行抽取,再把这个抽取出来的类C当做另一个类D中方法的参数,而这个方法就是完成之前那两个方法。使用时,只需建立D类对象,就可以使用子类重写的方法
interface PCI{ //C
public void open();
public void close();
}
class MainBoard{ //D
public void run(){
System.out.println("mainboard run ");}
public void usePCI(PCI p){//PCI p = new NetCard()//接口型引用指向自己的子类对象。
if(p!=null){
p.open();
p.close(); }
}
class NetCard implements PCI{ //A
public void open(){
System.out.println("netcard open");}
public void close(){
System.out.println("netcard close");
method();}
}
class SoundCard implementsPCI{ //B
public void open(){
System.out.println("SoundCard open");}
public void close(){
System.out.println("SoundCard close");}
}
class MainBoardDemo{
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
mb.usePCI(null);
mb.usePCI(newNetCard());
mb.usePCI(new SoundCard());}
}
Object类
Java中所有类的父类。该类定义的是所有对象都具备的功能,因此Object类中的方法可以被重载、重写。
toString():返回该对象的字符串表示
equals() :用于比较对象的引用指向的是否是同一个对象(比较的是地址)
对象比较的两种方法:
hashcode( ) | 根据对象所在的内存返回一个能够唯一代表这个对象的int值。两对象相等,hashcode( )返回值相等;两对象不相等,hashcode( )返回值不相等。 |
equals( ) | 比较的是地址,而不是两个对象是否逻辑相等。 |
通常通过覆盖hashcode( )和equals( )方法类达到比较自定义对象的目的。
public class Student(){
private String name;
private int age;
public student(name,age){
this.name=name;
this.age=age;
}
public bealoon equals(objectobj){
if(!(obj instanceof Student))
return false;
Student s=(Student)obj;
return (name.equals(s.name))&&(age==s.age);
}
public int hashCode(){
final int prime=31;
int result = 1;
result = result * prime + ((name==null)? 0 : name.hashCode());
result = result * prime +age
return result;
}
}
内部类
如果把内部类所在的类称为外部类,那么内部类同时具备类成员和类两个属性。可以分为成员内部类和局部内部类。
l 内部类可以直接访问外部类中的成员,包括private。因为内部类中有一个外部类的引用,格式:外部类名.this 如:外部类名.this.外部类变量/外部类方法
l 外部类要访问内部类,必须建立内部类对象。内部类一般在外部类的内部使用,很少在外部类之外的一个类中使用这个内部类。
1.成员内部类:当内部类定义在外部类的成员位置上
² 一般内部类都默认是非静态成员内部类
² 可以被任何成员修饰符修饰,如private、static、abstract、final。
若内部类被private修饰,就是将内部类在外部类中进行封装,避免了外部其他类使用
若内部类被static修饰(静态内部类),则该内部类就是一个类范畴的属性而不是作为外部类的对象了。此时只能直接访问外部类中的static成员,但可以通过new 外部类( ).成员的方式访问外部非static成员
² 当内部类中定义了静态成员,该内部类必须是static的;当外部类中的静态方法访问内部类时,内部类也必须是static的
² 在外部其他类中,访问内部类,需要创建外部类里面内部类的对象。成员内部类的全限定名是:外部类名.内部类名
直接访问非static内部类的成员时,格式:
Outer out=new Outer();
Inner in =out.new Inner();
//可以简写成
Outer.Inner in = newOuter().new Inner();
in.function();//访问非静态方法
Outer.Inner.staticFunction();//访问静态方法
直接访问static内部类的成员时,格式:
StaticInner in =new Outer.StaticInner();
in.function( );//访问非静态方法
StaticInner.staticFunction();//访问静态方法
2.局部内部类:内部类定义在外部类方法的局部位置上
² 可以直接在外部类方法中创建局部内部类的对象,来调用局部内部类的方法。
² 不能使用访问权限控制符和 static 修饰符;
² 可以直接访问外部类中的成员,但不可以访问外部类方法中局部的变量(和局部内部类等一个层次),除非这个变量被final修饰
class Outer{
int x = 3;
void method(final int a){
final int y = 4;//外部方法中局部的变量,被final修饰
class Inner{
void function(){
System.out.println(y);}}
new Inner().function();
}
}
3.匿名内部类:没有名字的内部类(无构造方法)。是内部类的简写格式,可以简化代码
² 定义匿名内部类的前提:该内部类必须继承一个抽象类或者实现接口。但是不能显式地继承或实现。所以匿名内部类其实就是一个匿名子类对象,而且是带内容的对象。
² 匿名内部类能使用访问权限控制符和 static 修饰符
² 格式:new 父类或者接口(){定义子类的内容}
² 如果多次调用匿名内部类中的方法,可以通过多态创建匿名内部类的对象再调用。
abstract class AbsDemo{
abstract void show();
}
class Outer{
int x = 3;
new AbsDemo(){
int num = 9;
void show(){System.out.println("num==="+num);}
}.show;
//利用多态实现多次调用匿名内部类方法
AbsDemo c=new AbsDemo(){
int num = 9;
void show(){System.out.println("num==="+num);}
}
c.show;
}
异常
Throwable
|--Error:严重的问题,一般不编写针对性的代码对其进行处理
|--Exceptoin:在运行时出现的一些异常,可以使用针对性的处理方式进行处理
|--RuntimeException
异常体系中的所有类以及建立的对象都具备可抛性。可以被throw和throws关键字所操作!
1.Throwable中方法(对捕获到的异常对象进行的常见方法操作)
String getMessage() | 获取异常信息 |
String toString() | 获取异常类名和异常信息 |
void printStackTrace() | 获取异常类名、异常信息以及异常的位置 |
String printStackTrace(PrintStream s) | 将异常内容保存在日志文件中 |
2.异常处理:try-catch-finally语句
try{需要被检测的代码;}
catch(异常类 变量){处理异常的代码;}
finally{一定会执行的语句;(通常是关闭资源代码,因为资源必须被释放!)}
² 有三种方式:try-catch-finally; try-catch; try-finally
² finally代码块只有一种情况不会被执行:在之前执行了System.exit(0) 系统退出
² 一个 try 块后能跟多个 catch 块。多个 catch 块的语法像这样:
3.throws和throw
throws:在方法声明中抛出异常,用在函数上,后面跟异常类名(可多个,用逗号隔开)
throw:在方法代码中抛出异常,用在函数内,后面跟异常对象
注:在函数内部使用throw,要么在内部try-catch处理(没有catch就没有被处理!);要么在函数上声明让调用者处理。(RuntimeException除外)
4.自定义异常:必须是继承Exception或者其子类!
一般通过构造函数定义异常信息,通过throw将自定义异常抛出。
class MyException extendsException{
MyException(String message){
super(message);}}
自定义负数异常
class FuShuException extendsException {
private int value;
FuShuException(String msg){
super(msg);}
}
class Demo{
int div(int a,int b)throwsFuShuException{
if(b<0)
//手动通过throw关键字抛出一个自定义异常对象
throw new FuShuException("出现了除数是负数的情况-",b);
return a/b;}
}
class MyExceptionDemo{
public static void main(String[] args) {
Demo d = new Demo();
try{
int x = d.div(4,-9);
System.out.println("x="+x);
}catch (FuShuException e){
System.out.println(e.toString());}
System.out.println("over");}
}
5. 异常有两种:
编译时异常 | 编译时,如果没有处理(没有throw也没有try),编译失败。该异常被标识,代表这可以被处理 |
运行时异常 | 编译时,不需要处理,编译器不检查。该异常的发生,建议不处理,让程序停止。需要对代码进行修正 |
RuntimeException类:运行时异常。
l RuntimeException以及其子类如果在函数中被throw抛出,可以不用在函数上声明;
l RuntimeException以及其子类如果在函数上被throws声明,可以不通过调用者进行处理。
l 该异常(RuntimeException类及其子类)发生,就是希望程序停止。因为在运行出现了无法继续运算的情况时,只有当停止程序后,才可对代码进行修正。因此自定义异常时:如果该异常的发生,无法在继续进行运算,就让自定义异常继承RuntimeException类。
6.异常在子父类覆盖中的体现:
l 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了特有的异常,就必须要进行try处理。绝对不能抛!
l 子类在覆盖父类时,如果父类方法抛出异常,那么子类只能抛出父类异常的子类或集合
7.总结:异常的处理原则
l 处理方式有两种:try或者 throws。
l 声明异常时,要声明为具体的异常,以便具体的处理。调用到抛出异常的功能时,声明几个,就处理几个。
l 一个try可对应多个catch。但是最好不要定义多余的catch块,多个catch块中的异常出现继承关系,父类异常的catch块放在最下面。
l catch内,需要定义针对性的处理方式。不要简单地定义e.printStackTrace或者输出语句
l 当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。
try{
throw new AException();
}catch (AException e){
throw e;}
如果异常不属于该功能出现的异常,并且处理不了。可以将异常转换后,再抛出和该功能相关的异常。 下左
try{ throw new AException(); }catch (AException e){ throw new BException(); } | try{ throw new AException(); }catch (AException e){ 先对AException处理; throw new BException();} |
如果异常不属于该功能出现的异常,但是可以处理。可将异常产生的和本功能相关的问题提供出去让调用者知道并处理;或将捕获异常处理后,再转换成新的异常抛出。上右
//有一个圆形和长方形,都可以获取面积。对于面积如果出现非法的数值,视为是获取面积出现问题。问题可以通过异常来表示。
class NoValueExceptionextends RuntimeException{
NoValueException(String message){
super(message);}
}
interface Shape{
void getArea();
}
class Rec implements Shape{
private int len,wid;
Rec(int len ,int wid){
if(len<=0 || wid<=0)
throw new NoValueException("出现非法值");
this.len = len;
this.wid = wid;}
public void getArea(){
System.out.println(len*wid);}
}
class Circle implementsShape{
private int radius;
public static final double PI = 3.14;
Circle(int radius){
if(radius<=0)
throw new NoValueException("非法");
this.radius = radius;}
public void getArea(){
System.out.println(radius*radius*PI);}
}
class ExceptionTest{
public static void main(String[] args) {
Rec r = new Rec(3,4);
r.getArea();
Circle c = new Circle(-8);
c.getArea();
System.out.println("over");}
}
包(package)
用来对类文件进行分类管理,给类提供多层命名空间。包名必须是合法的标识符名,写在程序文件的第一行。如果一个源文件不是在default package中,那么此源文件的第一有效行必须是能正确表示源文件所在包的package语句。
包的全限定名:从源代码根目录(src)开始,包名之间有点号隔开。
类的全限定名:类所在的包的全限定名.类名。
u 使用包中的类时都是要使用类的全限定名,包括创建类的对象等。但是使用类的引用则与全限定名没有任何关系。Carcar=new Common.car();
u 包与包之间的访问时:被访问的包中类的权限必须是public;类中的成员权限必须是public或者protected。protected是为其他包中的子类提供的一种权限
u 一个类使用同一个包中的其他类时,可以省略类的全限定名。
u 编译:编译包中的类时,需要进入源代码的根目录,然后根据类源文件的路径和源文件名进行编译,否则编译失败。
1.import语句:
u 两种语法格式:
引入一个类:import 类全限定名;
引入一个包中的所有类:import 包全限定名.*; 不包含子包中的类!
u 一个程序文件中只有一个package,但可以有多个import。
u 用来导入包中的类,而不是导入包中的包。
u Java编译器默认引入java.lang中的所有类。
u 当类中的源码中使用了没有全限定名的其他类,Java编译器寻找这个类的顺序规则:使用第1种格式引入的类;使用与这个类中同一个包中的类和使用第2种引入的类。
2.jar包
Java的压缩包。方便项目的携带和使用,只要在classpath设置jar路径即可
u 数据库驱动,SSH框架等都是以jar包体现的。
u 通过jar.exe工具对jar的操作:
创建jar包 | jar -cvf mypack.jar packa packb |
查看jar包 | jar -tvf mypack.jar [>定向文件] |
解压缩 | jar -xvf mypack.jar |
自定义jar包的清单文件 | jar –cvfm mypack.jar mf.txt packa packb |
多线程
1.进程与线程
进程:是一个正在执行中的程序,负责整个程序的内存分配
线程:在一个进程中负责一条执行路径。线程在控制着进程的执行。
多线程:一个进程中可以有多条执行路径
JVM启动时就启动了多个线程,至少有两个线程是可以确定的:
执行main函数的线程(主线程);负责垃圾回收的线程
Java给我们提供了对象线程这类事物的描述,Thread类。它有一些常用方法:
static Thread currentThead( ):
String getName( ):获取名称
static voidsleep(time)throws InterruptedException:
2.创建方法
(1)继承Thread类:
定义类继承Thread→覆盖Thread类中的run( )方法→创建类的对象调用start( )方法
run( )方法:用于存储线程要运行的代码;start( )方法:启动线程和调用run方法
(2)实现Runnable接口:
定义类实现Runnable接口→覆盖接口的run( )方法→创建Thread类的对象并将Runnable接口的子类对象作为参数传给Thread的构造函数→调用Thread对象的start( )方法
3.线程的四种状态:
4.同步(synchronized)
(1)导致线程出现安全问题的原因:多个线程访问出现延迟;线程随机性。
解决办法是,在执行操作共享数据语句的过程中,只能让一个线程都执行完后,其他线程才可以参与执行。java专业的解决方法就是:同步。
同步,解决了多线程的安全问题,但是耗费资源,降低程序的运行效率
(2)同步的前提:需要两个或者两个以上的线程+多个线程使用的是同一个锁。
(3)同步的两种表现形式:
同步代码块:synchronized(对象){需要被同步的代码};
同步函数:在函数上加上synchronized修饰符
同步可以解决安全问题的根本原因就在那个对象上。该对象就如同锁,只有持有锁的线程才可以在同步中执行。
Ø 区别:同步代码块使用的锁是任意对象(可以创建obj对象);同步函数使用的锁是this。
Ø 注意:对于static同步函数,使用的锁不是this。是类名.class,即该类的字节码文件对象
Ø 代码示例:this static 懒汉式
(4)死锁:同步中嵌套同步。
4.线程间通信:多个线程操作同一个资源,但是操作的动作不同。
字符串
String类
String类是对字符串的事物的描述,而字符串是一个特殊的对象,注意,字符串一旦被初始化就不可以被改变!因此,复制字符串等修改操作都是重新创建一个新的字符串!
String s1 = “abc”; //s1是一个常量,“abc”是一个对象
String s2 = newString(“abc”);
char chars[]={'a','b','c'};
String s3=new String(chars);
s1和s2有什么区别? s1在内存中只有一个对象,s2在内存中有两个对象。
String s1 = “abc”这个语句会先检查字符串常量池是否存放这个”abc”这个字符串对象,如果没有存在,那么就会在字符串常量池中创建这个字符串对象,如果存在直接返回该字符串的内存地址值。就一个对象
String s2= new String(“abc”) 这个语句首先会先检查字符串常量池中存不存在jack这个字符串对象,如果不存在就会创建,如果存在就返回内存地址值。创建了出来之后,new String这个语句就会在堆内存中开辟一个字符串对象。总共两个对象。
常用方法:
| 说明 | 方法 |
获取 | 获取字符串长度 | int length( ) |
返回指定位置的字符 | char charAt(int index) | |
返回的str在字符串中第一次出现的位置 | int indexOf(String str) | |
指定位置开始返回str在字符串中出现的位置 | int indexOf(String str, int fromIndex) | |
判断 | 字符串中是否包含某一个子串 | boolean contains(str) |
字符串中是否有内容 | boolean isEmpty( ) | |
字符串是否是以指定内容开头 | boolean startsWith(str) | |
字符串是否是以指定内容结尾 | boolean endsWith(star) | |
² 字符串内容是否相同 | boolean equals(str) | |
字符串内容是否相同,忽略大小写 | boolean equalsIgnoreCase( ) | |
转换 | 将字符数组转成字符串 | String(char[]) |
将字符数组中的一部分转成字符串 | String(char[],offset,count) | |
u 将字符串转成字符数组 | char[] toCharArray() | |
将字节数组转成字符串 | String(byte[]) | |
将字节数组中的一部分转成字符串 | String(byte[],offset,count) | |
u 将字符串转成字节数组 | byte[] getBytes( ) | |
将基本数据类型转成字符串 | static String valueOf(int/double/...) | |
将字符串转成大写或小写 | String toUpperCase( )/toLowerCase( ) | |
替换 | 将原来的字符串替换成的指定的字符串 | String replace(oldchar,newchar) |
切割 | 根据给定正则表达式的匹配拆分此字符串 | String[] split(string regex) |
子串 | 返回一个新的字符串(原字符串的子串) | String substring(begin) |
返回一个新的字符串(原字符串的子串) | String substring(begin,end) | |
除空 | 将字符串两端的多个空格去除 | String trim( ) |
比较 | ² 对两个字符串进行自然顺序的比较 | int compareTo(string) |
StringBuffer类
是一个字符串缓冲区,一个容器。字符串的组成原理就是通过该类实现的。它的特点是:
² 长度是可变;
² 可以字节操作多个数据类型;
² 最终会通过toString方法变成字符串。
常用方法:
添加:StringBuffer append():将指定数据作为参数添加到已有数据结尾处。
StringBufferinsert(index,数据):可以将数据插入到指定index位置。
删除:StringBuffer delete(start,end):删除缓冲区中的数据,包含start,不包含end。
StringBufferdeleteCharAt(index):删除指定位置的字符。
修改:StringBufferreplace(start,end,string); voidsetCharAt(int index, char ch) ;
获取:char charAt(int index);int indexOf(String str); Stringsubstring(int start, int end);
反转:StringBuffer reverse();
将缓冲区中指定数据存储到指定字符数组中:
voidgetChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
StringBuffer sb= newStringBuffer(“abcdef”);
char [] chs=new char[4];
sb.getChars(1,4,chs,1);//在缓冲区中,从1开始到4结束取字符,放在chs数组中,从1开始
for(intx=0;x<chs.length;x++){
System.out.println(“char[“+x+“]=“+char[x]+“;“);
}
结果:
char[0]= ;
char[1]=b;
char[2]=c;
char[3]=d;
//清空缓冲区
sb.delete(0,sb.length());
StringBuilder类
与StringBuffer用法几乎一致,区别:StringBuffer线程同步,StringBuilder线程不同步
Ø 三者比较
String:在字符串不经常变化时使用,如:常量的声明、少量的变量运算
StringBuffer:在字符串进行频繁的运算(拼接、替换、删除等),并且运行在多线程中
StringBuilder:在字符串进行频繁的运算(拼接、替换、删除等)时使用
Ø 字符串连接
+:String类使用的,返回新的字符串
Concat():String使用的,返回新的字符串
append():StringBuffer使用,返回的是原字符串缓冲区,不是新是字符串!
集合框架
集合:存储对象的容器,存储的是对象的引用
(1)与数组的比较
数组虽可以存储对象,但长度是固定的,集合长度是可变的
数组中还可以存储基本数据类型,而集合只能存储对象
集合和数组存储的都是对对象的引用,而不是对象本身
(2)特点:
只用于存储对象;
一个集合可以同时存储不同类型的对象;
长度是可变的。
(3)集合框架的常用接口及其子接口
Collection
|--List(列表):有索引,可存放重复元素,元素存取是有序的
|--ArrayList:底层数据结构是数组结构,查询速度快,增删速度慢
|--LinkedList:底层数据结构是链表结构,查询速度慢,增删速度快
|--Vector:底层数据结构是数组结构,线程安全,但速度慢,已被ArrayList替代
|--Set(集):无索引,不可以存放重复元素,元素存取是无序的
|--HashSet:底层数据结构是哈希表,存取速度快
|--TreeSet:底层数据结构是二叉树,可以对Set集合中的元素进行排序
|--LinkedHashSet:保留存储顺序, 并且可以过滤重复元素
Map:存储的是键值对,需要保证键的唯一性
|--Hashtable:底层数据结构是哈希表,线程安全,速度慢,不允许存放null键和null值
|--HashMap:底层数据结构是哈希表,速度快,允许存放null键和null值。
|--LinkedHashMap
|--TreeMap:底层数据结构是二叉树,对键进行排序,排序原理与TreeSet相同
1.Collection
1.1共性功能
添加:add(Object e); addAll(collection);
删除:remove(Objecte); removeAll(collection); clear();移除所有元素
判断:contains(Objecte);是否包含指定元素 isEmpty();
获取:iterator(); size();返回此collection 中元素的个数
获取交集:retainAll();
集合变数组:toArray();
1.2迭代器
迭代是对集合中的元素的一种取出方式。对 Collection 进行迭代的类,称其为迭代器。迭代器作为集合的对象,该对象比较特殊,不能通过new直接创建对象,该对象是以内部类的形式存在于每个集合类的内部。
Itreator接口是集合的迭代器接口类,定义了常见的迭代方法
boolean hasNext():判断集合中是否有元素,如果有元素可以迭代,就返回true
E next():返回迭代的下一个元素
void remove():从迭代器指向的集合中移除迭代器返回的最后一个元素
遍历迭代器有两种用法 :
for(Iterator iter =al.iterator();iter.hasNext(); ){ Object obj =iter.next(); } | Iterator iter =al.iterator(); while(iter.hasNext()){ Object obj =iter.next();} |
对于用法一,在for循环里,使用完了ite对象后释放了内存,更优化!
可使用迭代器清空集合
Collection coll = newArrayList();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
System.out.println(coll);
Iterator it =coll.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
注意事项:
l 迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
l 迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
l 迭代器的next方法返回值类型是Object,所以要记得强制类型转换!
1.3 List
1.3.1 List接口的特有方法
凡是可以操作角标的方法都是该体系特有的方法。
增:add(index,element); addAll(index,Collection);
删:remove(index);
改:set(index,element);
查:get(index); subList(from,to); listIterator(); intindexOf(obj):获取指定元素的位置
注:List集合特有的迭代器ListIterator是Iterator的子接口。Iterator只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口:ListIterator。
1.3.2 ArrayList
数组的内存空间地址是连续的。ArrayList底层维护了一个Object[]用于存储对象,默认数组的长度是10。可以通过 new ArrayList(20)显式的指定用于存储对象的数组的长度。当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来的容量的1.5倍。
由于ArrayList是数组实现,数组是可以直接按索引查找,所以查找时较快;在增和删的时候会牵扯到数组增容, 以及拷贝元素. 所以慢。
1.3.3 LinkedList
在内存中的地址不连续,需要让上一个元素记住下一个元素。所以每个元素中保存的有下一个元素的位置。虽然也有角标,但是查找的时候,需要从头往下找,显然是没有数组查找快的。但是,链表在插入新元素的时候,只需要让前一个元素记住新元素,让新元素记住下一个元素就可以了;删除元素时让前一个元素记住后一个元素, 后一个元素记住前一个元素。这样的增删效率较高。
特有方法介绍:
u 方法:addFirst(Ee); addLast(E e); removeFirst(); removeLast(); getFirst(); getLast();
u 数据结构:栈:先进后出 push() pop() 队列:先进先出offer() poll()
u 回逆序的迭代器对象:descendingIterator(); 返
u 如果集合中没有元素,获取或者删除元素式抛:NoSuchElementException
LinkedListlist = new LinkedList();
list.add("aa");
list.add("bb");
list.add("cc");
Iteratordit = list.descendingIterator();
while(dit.hasNext()) {
System.out.println(dit.next());}
}
用LinkedList模拟队列(先进先出)和和堆栈(后进先出)的数据结构
//堆栈(后进先出) 数据结构
LinkedListlist = new LinkedList();
// 压栈,先进后出
list.push("西游记");
list.push("三国演义");
list.push("石头记");
list.push("水浒传");
System.out.println(list);
// 弹栈
Stringstr1 = (String) list.pop();
System.out.println(str1);
Stringstr2 = (String) list.pop();
System.out.println(str2);
Stringstr3 = (String) list.pop();
System.out.println(str3);
Stringstr4 = (String) list.pop();
System.out.println(str4);
System.out.println(list.size());//0
System.out.println(list);//[]
//队列,先进先出
LinkedListlist = new LinkedList();
// 队列,先进先出
list.offer("西游记");
list.offer("三国演义");
list.offer("石头记");
list.offer("水浒传");
System.out.println(list);
// 出队列
System.out.println(list.poll());
System.out.println(list.poll());
System.out.println(list.poll());
System.out.println(list.poll());
System.out.println(list.size());
System.out.println(list.peek());// 获取队列的头元素,但是不删除
System.out.println(list.peekFirst());// 获取队列的头元素,但是不删除
System.out.println(list.peekLast());// 获取队列的最后一个元素但是不删除
1.4Set
1.4.1 HashSet
通过hashCode方法和equals方法来保证元素的唯一性!
如果元素的HashCode值相同,才会判断equals是否为true;
如果元素的hashcode值不同,不会调用equals。
class Person{
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;}
public int hashCode(){
System.out.println(this.name+"....hashCode");
return name.hashCode()+age*37;
}
public boolean equals(Object obj){
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
return this.name.equals(p.name) && this.age ==p.age;
}
}
class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a1",13));
Iterator it = hs.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.println(p.getName()+"::"+p.getAge());}}
}
1.4.2 TreeSet
通过compareTo或者compare方法中的来保证元素的唯一性
TreeSet的第一种排序方式:让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。
//按年龄给学生排序
class Student implementsComparable{
private String name;
private int age;
Student(String name,int age){
this.name = name;
this.age = age;}
public int compareTo(Object obj){
if(!(obj instanceof Student))
throw new RuntimeException("不是学生对象");
Student s = (Student)obj;
//当前元素和被比较元素比,大返回1在右边;等返回0;小返回-1在左边
if(this.age>s.age)
return 1;
if(this.age==s.age){
return this.namepareTo(s.name);}
return -1;
}
}
classTreeSetTest1 {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a1",13));
Iterator it = hs.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.println(p.getName()+"::"+p.getAge());}}
}
TreeSet的第二种排序方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的时候,让集合自身具备比较性。定义一个类,实现Comparator接口,覆盖compare方法。在集合初始化时,就有了比较方式:定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
//按姓名给学生排序
class MyCompare implementsComparator{
public int compare(Object o1,Object o2){
Student s1 = (Student)o1;
Student s2 = (Student)o2;
int num = s1.getName()pareTo(s2.getName());
if(num==0){
return new Integer(s1.getAge())pareTo(newInteger(s2.getAge()));
}//Integer具备compare方法
return num;}
}
class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet(MyCompare );//将比较器作为构造函数的参数传入
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a1",13));
Iterator it = hs.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.println(p.getName()+"::"+p.getAge());}}
}
注:当两种排序都存在时,以比较器为主!
2.泛型
JDK1.5后的安全机制。将运行期遇到的问题转移到了编译期,避免了强制类型转换
(1)用法:在使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展。而现在可以通过定义泛型来完成扩展。
public interface Comparable{
public int compareTo(Objecto);}
//加入泛型后
public interfaceComparable<T> {
public int compareTo(T o);}
注:
u 声明好泛型类型之后,集合中只能存放特定类型元素
u 泛型类型必须是引用类型,也就是说不能存放基本类型,而是存储基本类型的包装类
u 使用泛型后取出元素不需要类型转换
(1)泛型类:定义在类上:将<>放在类名后。泛型类定义的泛型,在整个类中有效。
(2)泛型方法:定义在方法上:那么泛型类的对象在明确操作的具体类型后,所有方法要操作的的类型就已经固定了。为了让不同方法可以操作不同类型,而且类型还不确定,可以将泛型定义在方法上!用法是将<>放在返回值类型之前,static之后。
特殊之处:静态方法不可以访问类上定义的泛型!如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
(3)泛型接口:定义在接口上:将<>放在接口名后
class Demo<T>{
public void show(T t){
System.out.println("show:"+t);}
public <Integer> void print(Integer i){
System.out.println("print:"+i);}
public static <W>void method(W t){//静态方法
System.out.println("method:"+t);}
}
class GenericDemo4 {
public static void main(String[] args) {
Demo <String> d = new Demo<String>();
d.show("haha");
d.print(5);
Demo.method("hahahahha");}
}
(4)泛型通配符
<?> 通配符。可以理解为占位符。
<? extends E>:可以接收E类型或者E的子类型。上限
<? super E>:可以接收E类型或者E的父类型。下限
//接收Number 类型或者Number的子类型
正确:Vector<? extends Number> x =new Vector<Integer>();
错误:Vector<? extends Number> x =new Vector<String>();
//接收Integer 或者Integer的父类型
正确:Vector<? super Integer> x =new Vector<Number>();
错误:Vector<? super Integer> x =new Vector<Byte>();
3.Map
3.1 共性功能
添加:put(K key, V value); putAll(Map<? extends K,?extends V> m)
删除:clear( );清空 remove(Object key)
判断:containsValue(Object value); containsKey(Object key); isEmpty()
获取:get(Object key);根据给定键获取值 size(); entrySet( ); keySet( )
Collection<V> values( );返回map集合中所有的值:Collection<String> coll = map.values();
put 添加重复的键值(值不同),会返回集合中原有(重复键)的值
Ø 区分:关于collection、map、StringBuffer
StringBuffer存储元素使用append方法,删除元素使用delete方法
Collection存储元素使用add方法,删除元素使用remove、clear方法
Map存储元素使用put方法,删除元素使用remove、clear方法
Collection是单列集合,map是双列集合;collection存的是一个元素,map存的是一对元素;map的键要保证唯一,不能重复;map没有直接取出元素的方法
Ø 小结:保证元素或键的唯一性
HashSet、HashMap通过覆盖hashCode和equals方法
TreeSet、TreeMap通过实现comparable或comparator接口
3.2取出方式
Map集合的取出原理:将map集合转成set集合,再通过迭代器取出!
方式一:keySet( )方法 Set<k> keySet= map.keySet() 将map中所有的键存入到Set集合。因为set具备迭代器,可以通过迭代方式取出所有的键,然后根据get(Object key)方法获取每一个键对应的值。
Map<String,String> map= new HashMap<String,String>();
map.put("02","zhangsan2");
map.put("03","zhangsan3");
map.put("01","zhangsan1");
Set<String> keySet =map.keySet();
Iterator<String> it =keySet.iterator();
while(it.hasNext()){
String key = it.next();
String value = map.get(key);
System.out.println("key:"+key+",value:"+value);}
方式二:values方法Collection<String>vs = map.values();直接获取map集合中所有值,但不能获取到key对象。
Map<Integer, String>map = new HashMap<Integer, String>();
map.put(1,"aaaa");
map.put(2,"bbbb");
map.put(3,"cccc");
Collection<String> vs= map.values();
Iterator<String> it =vs.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("value=" + value);}
方式三:entrySet( )方法Set<Map.Entry<k,v>>entrySet =map.entrySet() 将map集合中的映射关系存入到了set集合中,(该映射关系的数据类型是Map.Entry),可以通过迭代器方式遍历,再根据Map.Entry的getKey( )和getValue( )分别取出键或值
Map<String,String> map= new HashMap<String,String>();
map.put("02","zhangsan2");
map.put("03","zhangsan3");
map.put("01","zhangsan1");
Set<Map.Entry<String,String>>entrySet = map.entrySet();
Iterator<Map.Entry<String,String>>it = entrySet.iterator();
while(it.hasNext()){
Map.Entry<String,String> me = it.next();
String key = me.getKey();
String value = me.getValue();
System.out.println(key+":"+value);}
(3)Map集合扩充以相关示例:HashMap<String,List<Student>> 例程 示例1 示例2
4.集合框架工具类
这两个工具类中的方法都是静态的!
4.1 Collections
操作集合的工具类,常用方法:
(1)对List集合进行排序
sort(list); //对list进行排序,其实使用的事list容器中的对象的compareTo方法
sort(list,comaprator);//按照指定比较器进行排序
(2)对list进行二分查找:前提该集合一定要有序。
int binarySearch(list,key);//必须根据元素自然顺序对列表进行升级排序
int binarySearch(list,key,Comparator);
(3)对集合取最大值或者最小值:
max(Collection);max(Collection,comparator)
min(Collection);min(Collection,comparator)
(4)对list集合进行反转
reverse(list);
(5)可以将不同步的集合变成同步的集合。
Set synchronizedSet(Set<T> s)
Map synchronizedMap(Map<K,V> m)
List synchronizedList(List<T> list)
4.2 Arrays
操作数组的工具,常用方法:
(1)数组排序
sort(int[])
sort(char[])……
(2)二分查找,前提是数组需要有序
binarySearch(int[])
binarySearch(double[])
(3)将数组变成字符串
toString(int[])
(4)复制数组
copyOf();
(5)复制部分数组。
copyOfRange():
(6)比较两个数组是否相同。
equals(int[],int[]);
(7)将数组变成集合
List asList(T[]);
把数组变成list集合,可以使用集合的思想和方法来操作数组中的元素。可以使用的方法:contains,indexOf…但是不可以使用增删方法,add,remove。因为数组长度是固定的,会出现UnsupportOperationExcetion。如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。如果数组中的存入的引用数据类型,那么asList会将数组中的元素作为集合中的元素。
ArrayList<Integer>list = new ArrayList<Integer>();
list.add(4);
list.add(3);
list.add(1);
list.add(2);
list.add(3);
// 排序
Collections.sort(list);
// 折半查找的前提是排序好的元素
System.out.println(Collections.binarySearch( list , 8 ) ); // 找不到返回-插入点-1
// 反序集合输出
Collections.reverse(list );
// 求最值
System.out.println(Collections.max( list ) ); // 4
//fill() 使用指定的元素替换指定集合中的所有元素
Collections.fill(list, 5 );
System.out.println(list );
// 将数组转换为集合
Integeris[] = new Integer[]{6,7,8};
List<Integer>list2 = Arrays.asList(is);
list.addAll(list2 );
System.out.println(list );
// 将List转换为数组
Object []ins = list.toArray();
System.out.println(Arrays.toString( ins ) );
常用对象
1.System
描述系统一些信息,类中的方法和属性都是静态。
2.Runtime
主要描述的是应用程序运行的环境。并没有提供构造函数,说明不可以new对象。既有静态方法又有非静态方法。提供了获取本类对象的静态方法,返回值类型是本类类型。
3.Date与Calendar
Date 类封装的是系统的当前时间。但Date已经过时了
Calendar类是一个日历的类,封装了年月日时分秒时区。
日期格式化:将模式封装到SimpleDateformat对象中
4.Math
double d= Math.ceil(16.34);//ceil返回大于指定数据的最小整数。
double d1= Math.floor(12.34);//floor返回小于指定数据的最大整数。
long l =Math.round(12.54);//四舍五入
doubleradom();//返回带正号的 double 值,该值大于等于 0.0 且小于 1.0
Math.Random()*10;//[0,1)
Math.Floor(Math.Random()*10+1);//[0,10)
Math.Ceil(Math.Random()*10);//[0,10)
5. Properties——集合中和IO技术
hashtable的子类,也就是Map集合的一个子类对象。具备map集合的特点,里面存储的键值对都是字符串,没有泛型定义,是集合中和IO技术相结合的集合容器。该对象的特点是:可以用于键值对形式的配置文件。因此在加载数据时,需要数据有固定格式:键=值
setProperty(k,v);//添加
getProperty(k,v);//读取
load(InputStreaminStream);//将流中的数据加载进集合
store(OutputStreamout, String comments);//将集合加载进新的流中
Jdk新特性
1.高级for循环
对集合进行遍历。只能获取集合元素,不能对集合进行操作!
for(数据类型变量名 : 被遍历的集合(Collection)或者数组){ }
相较于传统for,高级for有一个局限性:必须有被遍历的目标!建议在遍历数组的时候用传统for,因为传统for可以定义脚标。
2.可变参数
只要将要操作的元素作为参数传递到参数列表中,隐式将这些参数封装成了数组,可以指定实际参数个数。
返回值类型函数名(参数类型…形式参数){执行语句}
声明:
在一个方法中,最多只能有一个可变参数。
可变参数只能放在参数列表的最后面。
调用:
当使用可变参数时,可以传0或多个参数。
当使用可变参数时,也可以传一个数组进去,就表示多个参数。
使用:
在方法内部使用时,就是在使用一个数组。
当调用时没有传参数时,这时在方法内部的参数数组是有值的(不为null),但长度为0.
3.静态导入
importstatic java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。
注意,当类名重名时,需指定具体的包名;当方法重名时,需指定具备所属的对象或者类
4.数的表示
以0b开头表示二进制数:
用下划线的方式对大的数据值进行分隔:
int y =21_345_678;
int z =0b111_0100_1010;
double d= 1234.56_789;
5.新式switch语句
传统的Switch里只可以接收的类型有:byte、short、int、char四种,在1.7以后switch里面的可以接收任意数据类型,它接收和比较的是这个对象的hash值
String sex=“男”;
switch(sex){
case "男":
System.out.println("先生 ,你好");
break;
case "女":
System.out.println("女士,你好");
break;
default:
System.out.println("你真好吗?");
}
6.泛型的简化
左边定义类型,右边不需要定义类型了,直接写<>即可
List<String>list = new ArrayList<>();
7.异常的多catch合并
一个try对应多个catch时,如果多个catch内部的处理方式都相同。可以把多个catch变成一个catch,在catch(异常1 | 异常2 异常变量名)
int[] arr= new int[3];
try {
int element = getElement(arr, 10);
System.out.println("element:"+ element);
} catch(final NullPointerException | ArrayIndexOutOfBoundsException e) {
System.out.println(e.toString());}
8.资源自动释放
try(必须是java.lang.AutoCloseable的子类对象){ }//资源的自动释放,将需要释放资源的代码放在try里面即可。一般流技术的对象都是这个接口的子类。
省去了调用close,省去了finally,简化了代码,但不要以为finally就不用了。
try(FileReaderfr = new FileReader("temp.txt");FileWriter fw = newFileWriter("temp2.txt")){ intch = fr.read();
fw.write(ch);
System.out.println(ch);
}
IO流
IO流用来处理设备之间的数据传输,Java对数据的操作是通过流,而流的对象都在IO包中
按操作数据分为:字节流与字符流(基于字节流,在内部融合了编码表)
流按流向分为:输入流,输出流
IO程序的书写:导入IO包中的类,进行IO异常处理,在finally中对流进行关闭
字节流与字符流的继承体系:
1.字节流(8位) 主要操作二进制数据,如声音、图像
有两个基类:InputStream(读) OutputStream(写)
InputStream
|--StringBufferInputStream:字符串缓冲区输入流
|--ByteArrayInputStream:字节数组输入流
|--FileInputStream:文件输入流
|--FilterInputStream:过滤器输入流
|--DataInputStream: 数据输入流
|--BufferedInputStream:缓冲区输入流
|--LineNumberInputStream:行号输入流 (PrintStream:输出流)
OutputStream与InputStream分类几乎一致,上面的括号中指出了不同。
(1)InputStream
提供了三种重载的read方法
public abstractint read( ):读取一个 byte 的数据,返回值是高位补0的 int 类型值。
int read(byte b[]):读取 b.length 个字节的数据放到b数组中。返回值是读取的字节数
int read(byte b[], int off, int len):从off始在输入流中读 len 个字节的数据存放到b 数组
(2)OutputStream
提供了三种重载的write方法
abstract voidwrite(int b) :先将 int 转换为 byte 类型,把低字节写入到输出流
void write(byteb[ ]):将字节数组b 中的字节写到输出流
void write(byteb[ ], int off, int len) :从off开始将数组b的len 个字节的数据写到输出流
(3)创建对象
创建FileInputStream、FileoutputStream对象
//方式1:通用
File f = new File(“lhf.txt”);
FileInputStream fs = newFileInputStream(f);
//方式2:通用
FileInputStream fs = newFileInputStream(“lhf.txt”);
//方式3:OutputStream特有
FileOutputStream f=newFileOutputStream("d:/abc.txt",true);
创建FilterInputStream、FilteroutputStream对象
DataInputStream din=newDataInputStream(fs);
BufferedInputStream bin=newBufferedInputStream(fs);
2.字符流(16位)
有两个基类:Reader Writer
(1)FileWriter:创建文件
创建流对象,建立数据存放文件:FileWriter fw = new FileWriter(“Test.txt”);
调用流对象的写入方法,将数据写入流:fw.write(“text”);
关闭流资源,并将流中的数据清空到文件中:fw.close();
区分:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
(2)FileReader:读取文件
创建流对象,将已存在的一个文件加载进流:FileReader fr = new FileReader(“Test.txt”);
创建一个临时存放数据的数组: char[] ch = new char[1024];
调用流对象的读取方法将流中的数据读入到数组中:fr.read(ch);
完整代码:
FileWriter fw = null; try{ fw = new FileWriter("Test.txt"); fw.write("text"); } catch (IOException e){ System.out.println(e.toString()); } finally{ If(fw!=null) try{ fw.close(); } catch (IOException e){ System.out.println(e.toString());} } | FileReader fr = null; try{ fr = new FileReader("c:\\test.txt"); char[] buf = new char[1024]; int len= 0; while((len=fr.read(buf))!=-1){ System.out.println(new String(buf,0,len));}} catch (IOException e){ System.out.println("read-Exception :"+e.toString());} finally{ if(fr!=null){ try{ fr.close();} catch (IOException e){ System.out.println("close-Exception :"+e.toString()); } |
注意:
u 定义文件路径时,可以用“/”或者“\\”;
u 在创建一个文件时,如果目录下有同名文件将被覆盖。也可以在已有文件的末尾处进行数据续写:new FileWriter("demo.txt",true);
u 在读取文件时,必须保证该文件已存在,否则出异常。
(3)字符流缓冲区:提高读写效率!对应类有:BufferedWriter和BufferedReader
BufferedWriter:将文本写入字符输出流。
该缓冲区中提供了一个跨平台的换行符:newLine();
BufferedReader: 从字符输入流中读取文本。
该缓冲区提供了一个一次读一行的方法:readLine(),方便于对文本数据的获取。当返回null时,表示读到文件末尾。readLine方法只返回回车符之前的数据内容,并不返回回车符。
//通过缓冲区复制一个.java文件。
BufferedReaderbufr = null;
BufferedWriterbufw = null;
try{
bufr = new BufferedReader(newFileReader("BufferedWriterDemo.java"));
bufw = new BufferedWriter(newFileWriter("bufWriter_Copy.txt"));
String line = null;
while((line=bufr.readLine())!=null){
bufw.write(line);
bufw.newLine();
bufw.flush();}
}catch(IOException e){
throw newRuntimeException("读写失败");}
finally{
try{
if(bufr!=null)
bufr.close();
}catch (IOExceptione){
throw new RuntimeException("读取关闭失败");}
try{
if(bufw!=null)
bufw.close();
}catch (IOExceptione){
throw newRuntimeException("写入关闭失败");}
}}}
注意:
u LineNumberReader extends BufferedReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
u 对于字符流,在没有加入缓冲区之前,读取文件是先把文件放进一个字节数组里,如果该数组的长度不为0,则用read方法通过读字节数组来读文件,这是一个字节一个字节的读!加入了缓冲区之后,如果缓冲区的长度不为空,对于BufferedReader则通过readLine方法一行一行地读取文件!
u 对于字节流,不管有没有加缓冲区,都是直接读写
3.转换流:
InputStreamReader:字节流通向字符流的桥梁
OutputStreamWriter:字符流通向字节流的桥梁。
将要写入流中的字符编码成字节,可以在构造函数中改变默认的字符编码集:
new OutputStreamWriter(OutputStreamout,"UTF-8")
4.标准输入输出流:
System类(lang包)中的字段:in,out 各代表标准的输入(键盘)和输出(显示器)
System.in的类型是InputStream
System.out的类型是PrintStream
标准输入:
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));
标准输出:
BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(System.out));
通过System类的setIn、setOut方法对默认设备进行改变, getProperties()获取系统信息
System.setIn(new FileInputStream(“1.txt”));//将源改成文件1.txt。
System.setOut(new FileOutputStream(“2.txt”));//将目的改成文件2.txt
System.getProperties();//获取系统信息
5.小结:操作流通过四个明确来完成:
ü 明确源和目的。
源:输入流(InputStream Reader) 目的:输出流(OutputStream Writer)
ü 明确操作的数据是否是纯文本。
是:字符流。不是:字节流。
ü 明确体系后再明确要使用哪个具体的对象。通过设备来进行区分:
源设备:内存(ArrayStream),硬盘(FileStream),键盘(system.in)
目的设备:内存(ArrayStream),硬盘(FileStream),控制台(system.out)
ü 明确是否要提高效率:加缓冲区
6.代码范例
(1)通过装饰设计模式来模拟一下readLine
import java.io.*;
class MyBufferedReader extendsReader{
privateReader r;
MyBufferedReader(Readerr){
this.r = r;}
public int read(char[] cbuf, int off, int len) throws IOException{
returnr.read(cbuf,off,len) ;}
public void close()throws IOException{
r.close();}
public String myReadLine()throws IOException{
StringBuildersb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
returnsb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
returnsb.toString();
return null;
}
public void myClose()throws IOException{
r.close();}
}
class MyBufferedReaderDemo{
publicstatic void main(String[] args) throws IOException{
FileReaderfr = new FileReader("buf.txt");
MyBufferedReadermyBuf = new MyBufferedReader(fr);
String line= null;
while((line=myBuf.myReadLine())!=null){
System.out.println(line);}
myBuf.myClose();
}
}
(2)复制文本:操作的是字符
FileWriterfw = null;
FileReaderfr = null;
try{
fw = new FileWriter("SystemDemo_copy.txt");
fr = newFileReader("SystemDemo.java");
char[]buf = new char[1024];
int len =0;
while((len=fr.read(buf))!=-1){
fw.write(buf,0,len);}
}catch(IOException e){
throw newRuntimeException("读写失败");
}finally{
if(fr!=null)
try{fr.close();
}catch (IOException e){
throw newRuntimeException("读取流关闭失败");}
if(fw!=null)
try{fw.close();
}catch (IOExceptione)
{ throw new RuntimeException("写入流关闭失败");
}}
(3)复制声音、图像:操作的是字节
BufferedInputStreambufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStreambufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
int by =0;
while((by=bufis.read())!=-1){
bufos.write(by);}
bufos.close();
bufis.close();
Flie类
用来将文件或者文件夹封装成对象(可以作为参数传递给流的构造函数),方便对文件与文件夹的属性信息进行操作。
1.File类常见方法有:
(1)创建
booleancreateNewFile():指定位置创建文件。如果该文件已经存在,则不创建,返回false。
boolean mkdir():创建文件夹。
booleanmkdirs():创建多级文件夹。
注:File f = new File("file.txt");文件并没有被创建!
(2)删除
booleandelete():删除失败返回false。如果文件正在被使用,则删除不了返回false。
voiddeleteOnExit();在程序退出时删除指定文件。
(3)判断
booleanexists() :文件是否存在
isHidden();文件是否为隐藏文件
isDirectory();路径名表示的文件是否是一个目录
isAbsolute();路径名是否为绝对路径名
(4)获取信息
getPath():获取路径名
getParent():获取绝对路径名父目录的路径名,如果获取是是相对路径,则返回 null
getAbsolutePath():获取路径名的绝对路径名
longlastModified():获取最后修改时间
String[] list():返回一个字符串数组,包括此抽象路径目录中的文件和目录
Flie[] listFile():返回一个Flie类数组,包括此抽象路径目录中的文件和目录。
String[ ] list(FilenameFilter filter):返回一个字符串数组,包括此抽象路径目录中满足指定过滤器的文件和目录
以匿名内部类的方式重写FilenameFilter接口的boolean accept(File dir, String name)方法:
String[]arr = dir.list(new FilenameFilter(){
publicboolean accept(File dir,String name){
returnname.endsWith(".bmp");}}
);
2.递归:函数自己调用自己。递归时要明确结束条件和递归次数,防止发生内存溢出!
3.代码范例:
(1)列出指定目录下的所有内容,即指定目录下文件或者文件夹,包含子目录中的内容
(2)删除目录
(3)创建java文件列表
示例:列出指定目录下所有内容 删除目录创建java文件列表
IO包中的其他类
(1)RandomAccessFile:
随机访问文件,自身具备读写的方法。该类不是IO体系中子类(它直接继承自Object),但它是IO包中成员。内部封装了一个数组,而且通过指针对数组的元素进行操作:可以通过getFilePointer(intx)获取指针位置,通过seek(intx) 和skipBytes(intx)改变指针的位置来达到随机访问的目的。
构造函数:RandomAccessFile(Filefile / String name, String mode)
通过构造函数可以看出,该类只能操作文件,而且操作文件还需有模式:只读r,读写rw等。
其实完成读写的原理就是内部封装了字节输入流和输出流。
如果模式为只读 r,则不会创建文件,而是会去读取一个已存在文件;如果该文件不存在,则会出现异常。
如果模式为我读写rw,如果操作的文件不存在,会自动创建;即使存在也不会覆盖。
有特殊的读写方法:readInt();readUTF();writeInt();writeUTF()……等 示例
(2)管道流:PipedInputStream和PipedOutputStream 示例
输入输出流可以通过connect()直接进行连接:in.connect(out) 注意需要结合线程使用!
(3)打印流:PrintWriter和PrintStream
可以直接操作输入流和文件
(4)序列流:SequenceInputStream
对多个流进行合并 示例:对象合并 对象分割
(5)操作对象:ObjectInputStream与ObjectOutputStream
被操作的对象需要实现Serializable接口,为保证读取,需要将对象所在的可序列化类的序列号固定,加上public ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
有特殊的读写方法:readInt();readUTF();writeInt();writeUTF()……等 示例
(6)操作基本数据类型:DataInputStream与DataOutputStream
(7)操作字节数组:ByteArrayInputStream与ByteArrayOutputStream
操作字节数组的流对象,用流的思想来操作数据
ByteArrayInputStream:在构造的时候,需要接收数据源,且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
因为这两个流对象操作的都是数组,并没有使用系统资源。所以,不用进行close关闭!
与前者相似,操作字符数组:CharArrayReader与CharArrayWrite
(8)操作字符串:StringReader 与 StringWriter
8.字符编码:通过子类转换流来完成InputStreamReader和OutputStreamWriter 默认GBK
编码:字符串变成字节数组。 String-->byte[]: str.getBytes(charsetName);
解码:字节数组变成字符串。 byte[] -->String: new String(byte[],charsetName);
如果操作文件时,编码的编码表和解码所用的编码表不是一个,通常可以用解码所用的编码表对数据再次编码,然后再用写入时的编码表解码。
图形用户界面:GUI
1.区分:
GUI:Graphical UserInterface(图形用户接口)。
用图形的方式,来显示计算机操作的界面,这样更方便更直观。
CUI:Command line UserInterface (命令行用户接口)
就是常见的Dos命令行操作。需要记忆一些常用的命令,操作不直观。
2.Java为GUI提供的对象都存在java.Awt和javax.Swing两个包中:
(1)java.Awt:Abstract Window ToolKit (抽象窗口工具包),需要调用本地系统方法实现功能。属重量级控件。
(2)javax.Swing:在AWT的基础上,建立的一套图形界面系统,其中提供了更多的组件,而且完全由Java实现。增强了移植性,属轻量级控件
继承关系图如下:
Container:为容器,是一个特殊的组件,该组件中可以通过add方法添加其他组件进来。
3. 布局管理器:容器中的组件的排放方式,就是布局。常见的布局管理器有:
FlowLayout(流式布局管理器):从左到右的顺序排列。 Panel默认的布局管理器!
BorderLayout(边界布局管理器):东,南,西,北,中 Frame默认的布局管理器!
GridLayout(网格布局管理器):规则的矩阵
GridBagLayout(网格包布局管理器):非规则的矩阵
CardLayout(卡片布局管理器):选项卡
4.创建简单的窗体
Framef = new Frame(“my window”);
f.setLayout(newFlowLayout());
f.setBounds(300,100,600,500);
f.setVisible(true);
5.事件监听机制
组成:事件源(组件)、事件(Event)、监听器(Listener)、事件处理(引发事件后处理方式)
使用方法:
第一步,确定事件源(容器或组件),通过事件源对象的addXXXListener()方法将侦听器注册到该事件源上。
第二步,该方法中接收XXXListener的子类对象,或者XXXAdapter(适配器)的子类对象。一般用匿名内部类来表示!在覆盖处理方法的时候,方法的参数一般是XXXEvent类型的变量接收。
第三步,事件触发后会把事件打包成对象传递给该变量,其中包括事件源对象。通过getSource()或者getComponent()获取。
通常,XXXListener有多个方法需要覆盖时,使用XXXAdapter就可覆盖自己想要实现的方法。
活动监听器ActionListener没有适配器,直接使用ActionListener。
f.addWindowListener(new WindowAdapter (){
public voidactionPerformed(WindowEvent e){处理方式}
});
but.addActionListener(new ActionListener(){
public voidactionPerformed(ActionEvent e){处理方式}
});
6.菜单
MenuBar,Menu,MenuItem
先创建菜单条,再创建菜单,在每一个菜单中建立菜单项,或者添加菜单中作为子菜单,子菜单中还可以再添加菜单或菜单项… 通过setMenuBar()方法,将菜单添加到Frame中。
示例:创建窗体 鼠标和键盘事件 路径打开 记事本
网络编程
1.网络参考模型与网络通讯要素
(1)
网络参考模型
(2)网络通讯要素
IP地址:用InetAddress封装成对象。是网络中设备的标识。不易记忆,可用主机名:localhost,本地回环地址:127.0.0.1
端口号:不需要封装成对象。是用于标识进程的逻辑地址,不同进程的标识。有效端口是0~65535,其中0~1024系统使用或保留端口。
传输协议:不需要封装成对象。是通讯的规则。常见协议有:TCP,UDP
2.UDP与TCP
UDP:面向无连接。将数据及源和目的封装在数据包中;不需要建立连接,是不可靠协议;每个数据报的大小在限制在64k(1024*64)内;速度快
TCP:面向连接。在连接中进行大数据量传输:字节流数据在两个Socket间通过IO传输;建立连接,形成传输数据的通道;通过三次握手完成连接,是可靠协议;效率会稍低
(1)UDP传输:DatagramSocket与DatagramPacket
发送端与接收端是两个独立的运行程序,哪个先启动没关系 UDP聊天程序
发送端:通过DatagramSocket建立updsocket服务→提供数据,通过DatagramPacket将数据封装到数据包中→通过socket服务的send方法,将数据包发送出去→关闭资源
DatagramSocket ds = new DatagramSocket();
byte[] by = “hello,udp”.getBytes();
DatagramPacket dp = new DatagramPacket(by, by.length,
InetAddress.getByName(“127.0.0.1”),10000);
ds.send(dp);
ds.close();
在发送端,要在数据包对象中明确目的地IP及端口!
接收端:通过DatagramSocket定义udpsocket服务→ 通过DatagramPacket定义一个数据包用以存储接收到的字节数据→通过socket服务的receive方法将收到的数据存入已定义好的数据包中→通过数据包对象将这些不同的数据取出→关闭资源
DatagramSocket ds = new DatagramSocket(10000);
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by,by.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String str = new String(dp.getData(),0,dp.getLength());
System.out.println(ip +"--"+str);
ds.close();
在接收端,要指定监听的端口!
(2)TCP传输:Socket和ServerSocket
客户端与服务端是两个独立的运行程序,需要先启动服务端
Socket是为网络服务提供的一种机制。通信的两端都有Socket,可以理解为插槽,所以网络通信其实就是Socket间的通信,而数据在两个Socket间是通过IO传输
客户端:通过Socket()创建客户端的Socket服务并指定要连接的主机和端口→连接成功,说明客户端与服务端建立了通道,通过IO流就可进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可→对于输出流,用write(string)写入数据→与服务端通讯结束后,关闭Socket
Socket s = new Socket(“192.168.1.1”,9999);
OutputStream out = s.getOutputStream();
out.write(“hello”.getBytes());
s.close();
服务端:通过ServerSocket()建立服务端的socket服务并监听一个端口→通过ServerSokcet的 accept( )方法获取连接过来的客户端对象。该方法式阻塞式的,没有连接就会等→客户端如果发过来数据,那么服务端就要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据→关闭客户端→关闭服务端(可选)
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
示例:服务端返回大写 客户端并发上传照片 用户登录 自定义浏览器
正则表达式
专门用于操作字符串的技术。可以通过一些符号的形式,简化代码并对字符串进行复杂操作。弊端:符号太多,阅读性越差。
1.常见符号:
中括号:[判断字符位上内容]
预定义字符:都带着反斜线\
\. : 任意字符。
\\d:数字
\\D : 非数字。[^0-9]
\\w : 单词字符[a-zA-Z_0-9]
边界字符:
^ : 行开头。
$ : 行结尾。
\b : 单词边界。
数量词:必须结合内容。
X?: X内容出现零次或一次。
X*: X内容出现零次或多次。
X+: X内容出现一次或多次。
X{n}:X内容出现n次。
X{n,}:X内容出现至少n次。
X{n,m}:X内容出现n到m次。
2.常用功能:匹配 切割 替换 获取
(1)匹配:使用String类中的booleanmatches( )方法。
Stringnumber = "18600001111";
Stringnumber_regex = "1[358]\\d{9}";
System.out.println(number+ ":"+ number.matches(number_regex));
(2)切割:使用String类中的String[]split() 方法。
//对空格进行分割
Stringstr = "zhangsan lisi wangwu";
Stringstr_regex = " +";
叠词就是后者和前者一致。前者还是任意,必须后者在复用前者的内容。正则的复用用的是 () 来封装的,虽然没有名字,但是会自动的给这些小括号进行了编号,从1开始,称之为正则表达式中的组。通过组的编号就可以调用指定的组,进行复用。
对叠词进行切割:(.)\\1+
组嵌套技巧:从左起数有几个左括号有几组。
Stringstr 1= "zhangsan#######lisi@@@@@@@@@@wangwu";
Stringstr_regex1 = "(.)\\1+";
String[]arr = str.split(str_regex);
for(Strings : arr){
System.out.println(s);
}
(3)替换:使用String类中的StringreplaceAll(regex ,string)方法。
//将将叠词替换成其中的一个 ,如多个###### 用#替换。
String str= "qwer######tyuio&&&&&&&psdfg";
str =str.replaceAll("(.)\\1+", "$1");
//将论坛帖子的联系方式数组都替换 ***
Stringstr = "q芳龄:20wer86000011190000ghjkl";
str =str.replaceAll("\\d{5,}", "***");
//将电话中的中间四位替换成****
Stringstr = "18600001111";
str =str.replaceAll("(\\d{3})(\\d{4})(\\d{4})", "$1****$3");
(4)获取:使用正则对象pattern 使用起来分成三步
将正则表达式字符串编译成正则对象pattern→通过pattern对象获取Matcher对象(匹配器对象)再通过匹配器对象对字符串进行规则的匹配,结果都在匹配器中→通过匹配器对象的功能获取结果。
Pattern p=Patternpile("a*b"); //规则
Matcher m= p.matcher("aaaaab"); //匹配
boolean b= m.matches();
while(m.find()){
System.out.println(m.group());
}
Matcher:匹配方法三个:matches();lookingAt(): find();
通过find找到匹配规则的字符串后,可以通过start() end()获取子串对应的索引,也可以通过group()直接获取子串。
3.代码示例
(1)对ip地址排序
应该先让这些ip地址的每一段的位数都是3位,这样才可以通过字符串自然排序。不足3位用0补,每一段要补的0的个数也不一致。咋补呢?干脆,按照每一段最大的的补零数去补,保证每一个段至少有三位。然后每一段只取最后三位。最后如果前面是0则替换。
Stringstr_ips = "127.0.0.1 3.3.3.3 192.168.104.23 10.10.10.10";
//先每一段都补两个0.
str_ips =str_ips.replaceAll("(\\d+)", "00$1");
//只保留三位。
str_ips =str_ips.replaceAll("0*(\\d{3})", "$1");
String[]ips = str_ips.split(" +");
//排序
Arrays.sort(ips);
for(Stringip : ips){
System.out.println(ip.replaceAll("0*(\\d+)","$1"));
}
(2)校验邮箱
Stringmail = "abc12@sina";
Stringregex = "[a-zA-Z_0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+){1,3}";
System.out.println(mail+":"+mail.matches(regex));
(3)网页爬虫
反射技术
动态的获取指定的类以及动态的调用类中的内容。以前,先有类,再new对象。有了反射后:先new对象,至于new哪个类由用户通过配置文件传递。即在没有类之前就将创建对象的动作完成了。
好处:大大的提高了程序的扩展性。
应用:当使用的类不确定的时候,可以通过配置文件告诉应用程序,只要应用程序中使用反射技术即可。
1.Class:描述字节码文件的类。将类中的成员封装成对象,然后对这些成员对象进行操作,提供对这些成员操作的方法
classClass(){
Field field; //将字段封装成Field对象
Constructor cons; //将构造器封装成Constructor对象
Method method; //将方法封装成Method对象
setXXX();
getXXX();
}
要想获取字节码文件中的成员,必须要先获取字节码文件对象:Class。
获取字节码文件对象的方式:
(1)通过Object类中的getClass方法。
Person p= new Person();
Classclazz = p1.getClass();
System.out.println(clazz.getName());//获取类的名字。
虽然通用,但是前提必须有指定类,并对该类进行对象的创建,才可以调用getClass方法。
(2)使用任意数据类的一个静态成员class。
Classclazz = Person.class;
不用new对象,但需要使用具体的类。
(3)反射:使用Class类中的forName方法给定类名的类来获取对应的字节码文件对象。通过newInstance()就可以创建字节码对象所表示的类的实例。
StringclassName = "cn.itcast.domain.Person";
Class clazz= Class.forName(className);
只要知道类的名字就可,注意此时的类名要指定全限定名作为字符串。
2.获取
(1)获取给定类的构造器,即动态创建对象。有两种方式:
默认调用空参数的构造函数:newInstance()
Objectobj = clazz.newInstance();
调用含参的构造函数:先通过getConstructor()方法获取构造器对象,然后在通过构造器创建对象并初始化。
Constructorcons = clazz.getConstructor(String.class,int.class);
Objectobj = cons.newInstance("wangwu",23);//Person p = newPerson("lisi",21);
类中需定义public的构造函数。如果没有对应的构造函数,会报InstantiationException;如果有但是权限不够,会报IllegalAccessException
(2)获取字段暴力访问字段
//获取age字段对象。
StringfieldName = "age";
Fieldfield = clazz.getField(fieldName);//获取是公共的字段。
Fieldfield = clazz.getDeclaredField(fieldName);//获取所有字段
//对其进行值的设置,必须先有对象。
Objectobj = clazz.newInstance();
field.setAccessible(true);//取消权限检查,暴力访问。一般不访问私有!
field.set(obj,30);
System.out.println(field.get(obj));
getXXX:获取都是类中公共的成员。
getDeclaredXXX:获取本类中已有的成员。
(3)获取方法
//获取非静态方法
String methodName= “show”;
Methodmathod = clazz.getMethod(methodName,String.class,int.class);
Objectobj = clazz.newInstance();
mathod.invoke(obj,”laowang”,23);
//获取静态方法
String methodName= “staticShow”;
Methodmathod = clazz.getMethod(methodName,String.class,int.class);
mathod.invoke(null,”laowang”,23);
注意:获取非静态方法必须要创建对象,获取静态方法可以不创建对象!
设计模式
1.单例设计模式
一个类在内存中只有一个对象
第一步,将构造函数私有化,避免其他程序建立本类对象;第二步,在类中创建一个本类的静态私有对象,让其他程序可以访问到本类对象;第三步,提供一个公共的静态方法可以获取到该对象。
饿汉式:直接初始化对象。Single类一进内存,就已经创建好了对象。
懒汉式:先建立对象再初始化。Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。
3.模板方法设计模式:在定义功能时,功能的一部分是确定的,一部分是不确定的。确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去。由该类的子类去完成。
装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类将已有对象传入,基于已有的功能提供加强功能。那么这个自定义的类称为装饰类。装饰类通常会通过构造方法接收被装饰的对象。对于这个被传入的对象,可以找到其参数的共同类型,通过多态的形式提高扩展性。
装饰模式比继承要灵活。避免了继承体系臃肿,而且降低了类于类之间的关系。示例
HTML
l Html是通过标签来定义的语言,代码都是由标签所组成。
l Html代码不用区分大小写。
l Html代码由<html>开始</html>结束。里面由头部分<head></head>和体部分<body></body>两部分组成。
l 头部分是给Html页面增加一些辅助或者属性信息,它里面的内容会最先加载。体部分是真正存放页面数据的地方。
l 多数标签都是有开始标签和结束标签,其中有个别标签因为只有单一功能,或者没有要修饰的内容可以在标签内结束。
l 想要对被标签修饰的内容进行更丰富的操作,就用到了标签中的属性,通过对属性值的改变,增加了更多的效果选择。
l 属性与属性值之间用“=”连接,属性值可以用双引号或单引号或者不用引号,一般都会用双引号。或公司规定书写规范。
l 格式:<标签名 属性名='属性值'> 数据内容 </标签名>
<标签名 属性名='属性值' />
l 操作思想:
为了操作数据,都需要对数据进行不同标签的封装,通过标签中的属性对封装的数据进行操作。
标签就相当于一个容器。对容器中的数据进行操作,就是在再不断的改变容器的属性值。
排版
<!-- --> HTML的注释
<br /> 换行标签
<hr /> 一条水平线
color: 颜色值的写法:两种
1)直接写英文的单词(red green blue)
2)RGB三原色(red green blue) #ab1255
width:宽度值两种写法:
1)200px;
2)可以写百分比,跟着浏览器的大小改变
字体<font>
1. 字体标签:<font>
<font size=5 color=red>字体标签示例</font>
2. 标题标签:<h1><h2>…..<h6>
就是某个字号和粗体的组合。
3. 特殊字符:
< | < | > | > | & | & |
" | “ | ® | ® | © | © |
™ | ™ | | 空格 |
|
|
列表 <list>
4. 列表标签:<dl>
上层项目:<dt>
下层项目:<dd>
有序标签:<0l>
无序标签:<ul>
<li>:具体项目内容标签。此标签只在<ol> <ul>中有效。通过type属性更改项目符号
<dl>
<dt>游戏名称</dt>
<dd>星际争霸</dd>
<dd>红色警戒</dd>
<dt>部门名称</dt>
<dd>技术部</dd>
<dd>培训部</dd>
</dl>
效果:
图像 <img>
5. 图像标签:<img>
<imgsrc=”1.jpg” align=”middle” border=”3” alt=”图片说明文字”/>
Src:连接一个文件; align:属性定义图片的排列方式; border用来设置图像的边框;
alt:图片的说明文字
6. 图像地图:<map>
应用:当要在图像中选取某一部分作为连接的时候。如:中国地图每个省所对应的区域。
map标签要和img标签联合使用。Href是超连接
<imgsrc="Sunset.jpg" alt="图片说明文字" usemap="#Map" />
<map >
<area shape="rect"coords="50,59,116,104" href="1.html" />
<area shape="circle"coords="118,203,40" href="2.html" />
</map>
表格 <table>
7. 表格标签:<table>
标题标签:<caption>,给表格提供标题。
表头标签:<th>,一般对表格的第一行或者第一列进行格式化,就是粗体显示。
行标签:<tr>
单元格标签:<td>,加载行标签的里面。可以简单理解为,先有行,在行中在加入单元格。
<table border="1" width=”40%”> <caption>表格标题</caption> <tr><!-- 第一行 --> <th>姓名</th> <th>年龄</th> </tr> <tr algin=”center”><!-- 第二行 --> <td>张三</td> <td>23</td> </tr> </table> |
效果:
每个表格可以有一个表头、一个表尾和一个或多个表体(即正文),分别以THEAD、TFOOT和TBODY元素表示。
THEAD、TFOOT包含关于表格列的信息。
TBODY作用是:可以控制表格分行下载,从而提高下载速度!在需要分行下载处加上<tbody>和</tbody>。(网页的打开是先表格的的内容全部下载完毕后,才显示出来,分行下载可以先显示部分内容,这样会减少用户等待时间。)
超链接 <a>
8. 超链接:<a href=””>
<a href=”http://www.sina”>新浪</a>
9. 定位标记:<a name=””>
一般在本页面中使用,当网页内容过长,定位标记会比拖动滚动条方便快捷。定位标记要和超链接结合使用才有效。
<a name=”标记”>标记位置</a>
<p>……<!--很多空行以制造网页过长的效果 -->
<a href=”#标记”>返回标记位置</a>
注:使用定位标记时一定在href值的开始加入#标记名。
框架 <framsset>
10. 框架标签:<frameset>
一般为了代码的可读性,会放在到<head>和<body>之间。
<frameset rows="10%,*">
<frame src="1.html"name="top" />
<frameset cols="30%,*">
<framesrc="2.html" name="left" />
<frame src="3.html"name="right" />
</frameset>
</frameset>
效果:
注:当框架大小不想被鼠标拖动而改变,可以在frame标签中加入noresize属性,这个属性没有属性值,称为标记属性,加上就为固定。在XHTML的规范中,所有的属性都要有属性值,那么标记属性的属性值就是自身,如:noresize=”noresize”;
11. 画中画标签:<iframe>
<iframe src=”1.html” >
很遗憾,画中画你没有看到,因为你的浏览器不支持iframe标签。
</iframe>
一般不显示,作为恶意链接!
表单 <form>
12. 表单标签:<form>
最常用的标签,用于与服务器端的交互。 需要指定属性name value属性可以理解成值
(1)<input>:输入标签. 接收用户输入信息。
其中的type属性指定输入标签的类型。
l 文本框 text。输入的文本信息直接显示在框中。
l 密码框 password。输入的文本以原点或者星号的形式显示。
l 单选框 radio 如:性别选择。
l 复选框 checkbox 如:兴趣选择。
l 隐藏字段 hidden 在页面上不显示,但在提交的时候随其他内容一起提交。
l 提交按钮 submit 用于提交表单中的内容。
l 重置按钮 reset 将表单中填写的内容设置为初始值。
l 按钮 button 可以为其自定义事件。
l 文件上传 file 后期扩展内容,会自动生成一个文本框,和一个浏览按钮。
l 图像 image 它可以替代submit按钮。
(2)<select>:选择标签 提供用户选择内容。如:用户所在的省市。size 属性为显示项目个数。
(3)<option>:子项标签 属性 selected 没有属性值,加在子项上,子项就变成默认被选项。
(4)<textarea>:多行文本框。如:个人信息描述。
(5)<label>:用于给各元素定义快捷键。
<form>
用户名称:<input type="text"name="username" value="hehe" /><br/>
输入密码:<input type="password"name="psw" /><br/>
选择性别:<input type="radio"name="sex" value="nan" />男
<input type="radio"name="sex" value="nv" checked="checked"/>女<br/>
选择技术:<input type="checkbox"name="tech" value="java" />JAVA
<input type="checkbox"name="tech" value="html" />HTML
<input type="checkbox"name="tech" value="css" />CSS<br/>
一个按钮:<input type="button" value="有个按钮" οnclick="alert('有个按钮,我弹!')"/><br/>
隐藏组件:<input type="hidden"name="zhangsan" value="20"/><br/>
选择文件:<input type="file"name="file" /><br/>
图片组件:<input type="image"src="1.jpg" /><br/>
选择国家:
<selectname="country">
<optionvalue='none'>--选择国家--</option>
<optionvalue="cn" selected="selected">中国</option>
<optionvalue="usa">美国</option>
<optionvaue='en'>英国</option>
</select>
<br/>
个人介绍:<textarea rows="4"cols="20"></textarea>
<inputtype="submit" value="提交"/><input type="reset" />
</form>
效果:
表单提交:
action属性值:指定表单数据提交的目的地(服务端)
method属性值:指定表单数据的提交方式。默认get
get和post这两种最常用的提交方式的区别:
区别1:地址栏是否显示信息。
GET提交,将提交的数据显示在地址栏。POST提交,提交数据不显示在地址栏。
区别2:敏感信息是否安全。
GET提交,提交敏感信息不安全。POST提交,提交敏感信息安全。
区别3:数据的体积。
GET提交,信息存储到地址栏,存储的信息体积有限。POST提交,可以提交大体积数据信息。
区别4:提交信息的http封装形式不同。
GET提交,将提交信息封装到了请求行。POST提交,将提交信息封装到了请求体。
注意:通常表单使用post提交,因为编码方便。
和服务端交互有三种方式:
地址栏输入:get
超链接:get
表单:get post
问题一:如果表单加入了增强型的校验(只有所有选项都符合规则的情况下,才可以提交),服务端收到数据后,还需要校验吗?
需要,因为客户端有可能避开校验,提交错误的数据到服务端,导致不安全
问题二:服务端如果进行校验,页面还需要做校验吗?
需要,为了减轻服务端的压力,同时为了增强用户的体验效果
头标签
头标签都放在<head></head>头部分之间。包括:title base meta link
13. <title>:指定浏览器的标题栏显示的内容。
14. <base>:
href 属性:指定网页中所有的超链接的目录。可以是本地目录,也可以是网络目录。注意值得结尾处一定要用/表示目录。只作用于相对路径的超链接文件。
target 属性:指定打开超链接的方式。如_blank 表示所有的超链接都用新窗口打开显示。
15. <meta>:
name 属性:网页的描述信息。当取keywords时,content属性的内容就作为搜索引擎的关键字进行搜索。
http-equiv 属性:模拟HTTP协议的响应消息头。
<meta http-equiv="refresh"content="3;url=http://www.sina" />
表示打开此页面3秒后自动转到新浪页面。
16. <link>:
rel 属性:描述目标文档与当前文档的关系。
type 属性:文档类型。
media:指定目标文档在哪种设备上起作用。
Other
17. <marquee> 让内容动起来。
direction 属性:left right down up
behavior 属性:scroll alternate slide
18. <pre>:可以将文本内容按在代码区的样子显示在页面上。
CSS
CSS是层叠样式表用来定义网页的实现效果,将网页内容和显示样式进行分离。可以解决html代码对样式定义的重复,提高了后期样式代码的可维护性,并增强了网页的现实效果功能。
n html中定义了用来专门封装区域的标签:
<div>:封装区域后有换行。
<span>:封装区域后无换行。
<p>:封装区域后有换行,且上下有空行。
n 所以标签可以分为两类:
块级标签(元素):div,dl,table,p…
行内标签(元素):span,a,input,img.select…
那么CSS和Html是如何在网页代码中相结合的呢?有四种实现方式:
1.style属性方式:利用标签中style属性来改变每个标签的显示样式。
<p style="background-color:#FF0000; color:#FFFFFF">
p标签段落内容。
</p>
该方式比较灵活,但是对于多个相同标签的同一样式定义比较麻烦,适合局部修改。
2.style标签方式:(内嵌方式)在head标签中加入style标签,对多个标签进行统一修改。
<head>
<style type=”text/css”>
p { color:#FF0000;}
</style>
</head>
该方式可以对单个页面的样式进行统一设置,但对于局部不够灵活。
3.导入方式:前提是已存在一个定义好的CSS文件。网页的一部分样式需要用到,就用到这种方式。
css_1.css:
@import url("div.css");
@import url("span.css");
span.css:
@CHARSET "UTF-8";
span{
color:#FF0000;
background-color:#FFFF66;
}
div.css:
@CHARSET "UTF-8";
div {
color: #00FFFF;
background-color: #000000;
font-size:24px;
}
<!--导入-->
<style type="text/css">
@importurl(css_1.css);
</style>
注:url括号后面必须要用分号结束。如果导入进来的样式与本页面定义的样式重复,以本页定义样式为准。也就是本页面的样式覆盖了原样式!
当多段代码相同时,将代码抽取成一个样式style
当多页面样式相同时,将样式抽取在一个样式文件中。
4.链接方式:
通过head标签中link标签来实现,前提也是先要有一个已定好的CSS文件。
<link rel="stylesheet" type="text/css"href="css_1.css" media="screen" />
注:可以通过多个link标签链接进来多个CSS文件。重复样式以最后链接进来的CSS样式为准。
总结CSS代码格式:选择器名称 { 属性名:属性值;属性名:属性值;…….}
属性与属性之间用分号;隔开
属性与属性值直接按用冒号:连接
如果一个属性有多个值的话,那么多个值用空格 隔开。
选择器:就是指定CSS要作用的标签,那个标签的名称就是选择器。意为:选择哪个容器。
选择器共有三种:
a) html标签名选择器。使用的就是html的标签名。
b) class选择器。其实使用的标签中的class属性。用.标记
c) id选择器。其实使用的是标签的中的id属性。用#标记
每一个标签都定义了class属性和id属性。用于对标签进行标识,方便对标签进行操作。在定义的中,多个标签的class属性值可以相同,而id值要唯一,因为JavaScript中经常用。
优先级:标签选择器<类选择器<ID选择器<style属性
1.class选择器:在标签中定义class属性并赋值。在style中通过标签名.class{}或者.class{}对该标签进行样式设置。
相同标签设置不同样式的时候,用class进行区分。
p.pclass_1 {color:#FF0000;}
p.pclass_2 {color:#0000FF;}
<p class="pclass_1">P标签样式</p>
<p class="pclass_2">P标签样式</p>
不同标签进行相同设置的时候,用class进行统一定义。
.classname {color:#00FF00;}
<p class="pclassname">P标签样式</p>
<div class="pclassname">DIV标签样式</div>
2.id选择器:与class选择器类似,但格式不同,选择器的名称为:#id值。
#pid { color:#0000FF;}
<p id="pid">P标签样式</p>
注:多个标签同样可以定义相同的id值,但是对于JavaScript对标签元素的获取就会出错。所以形成习惯,确保id值的唯一性对于以后的数据库设计也很有好处。
3.扩展选择器:
(1)关联选择器
标签是可以嵌套的,可以让相同标签中的不同标签显示不同样式。嵌套关系,用空格隔开
p { color:#00FF00;}
p b { color:#FF000;}
<p>P标签<b>刘德华</b>段落样式</p>
<p>P标签段落</p>
(2)组合选择器
对多个不同选择器进行相同样式设置。并列关系,用逗号隔开
p,div { color:#FF0000;}
<p>P标签显示段落。</p>
<div>DIV标签显示段落</div>
(3)伪元素选择器
其实就在html中预先定义好的一些选择器。称为伪元素。是因为CSS的术语。
格式:标签名:伪元素.类名 标签名.类名:伪元素
a:link{} 超链接未点击状态。
a:visited{}被访问后的状态。
a:hover{}光标移到超链接上的状态(未点击)。
a:active{}点击超链接时的状态。
使用顺序 L – V – H - A
p:first-line{}段落的第一行文本。
p:first-letter{}段落中的第一个字母。
p:focus{} 具有焦点的元素。
XML
1.概述
HTML:超文本标记语言
XHTML:是可扩展的超文本标记语言
XML:可扩展标记语言
(1)XML的简介:
n 可扩展
n 标记语言 HTML:显示数据;XML传输数据
n 标签自定义
n 一种通用的数据交换格式
n 用于描述、保存有关系的数据
n XML中的数据必须通过软件程序来解析执行或显示
(2)版本:XML1.0(使用该版本) XML1.1(不向下兼容)
(3)应用:
n 作为配置文件。
n 在系统与系统之间进行数据的传输
2.语法
2.1文档声明
(1)最简单的写法: <?xmlversion="1.0" ?>
必须出现在xml文件的第一行和第一列的位置!
(2)属性:
version="1.0" XML的版本(必写)
encoding="UTF-8" 编码集(可选)
standalone="yes/no" 文件是否独立(默认no:不独立,可以引入外部的文件)(可选)
(3)关于乱码
产生的原因:保存文件时和打开文件时采用的编码不一致。
解决办法:保存文件可打开文件采用的编码一致就ok。(MyEclipse不会产生乱码问题)
2.2元素
(1)写法: 一个标签包括开始标签和结束标签(不能省)
包含标签主体: <abc>文本</abc>
不包含标签主体:<abc/>
u 可以嵌套,但不能交叉嵌套! <mytag1><mytag2></mytag1></mytag2>╳
u 必须有,并且只能有一个根元素!
u 对于标签中出现的所有空格和换行,XML解析程序都会当作标签内容进行处理
(2)命名规范:
u 区分大小写 <a>、</A>代表两个标签
u 不能以数字和-中划线开头 <1a>、<-a>╳
u 不能以XML(Xml XML xml)开头 <xmlaa>╳
u 不能包含空格和冒号
2.3属性
u 自定义,命名规范同元素
u 一个元素可有多个属性,但在同一个元素上,不能有相同的属性
u 必须使用引号,可以使用双引号或者单引号
2.4注释
u 和HTML的注释相同:<!--XML的注释 -->
u 注释不能嵌套。 <!-- XML的注释<!--XML的注释 --> -->╳
2.5特殊字符
特殊字符写法同html
2.6CDATA
u 把标签中的内容作为字符串。
u 写法:<![CDATA[内容]]>
<![CDATA[
<itcast>www.itcast</itcast>
]]>
以上划线部分被当做普通文本而不是标签
2.7处理指令
3.约束
XML技术中,可以编写一个文档来约束一个XML的书写规范,这个文档称之为约束。
两个概念:
格式良好的XML:遵循XML语法的XML
有效的XML:遵循约束文档的XML
3.1 DTD
(1)混个面熟
(2)DTD与XML的关联方式
u 在XML的文件中直接书写DTD的代码
<!DOCTYPE根节点 [
DTD的代码
]>
u 引入本地的DTD文件
<!DOCTYPE根节点 SYSTEM "DTD文件的地址">
u 引入网络上的DTD文件
<!DOCTYPE根节点 PUBLIC "DTD文件名称" "DTD文件的地址">
(3)元素定义
<!ELEMENT元素名称 使用规则>
u 使用规则:
(#PCDATA) 字符串
EMPTY 空
ANY 任意的
(子元素) 子元素
u 子元素:
子元素之间的关系
, 子元素出现是有顺序的
| 子元素只能出现一个
( ) 给元素分组
子元素出现的次数
+ 子元素出现1次或多次
* 子元素出现0次或多次
? 子元素出现0次或1次
(4)属性定义
<!ATTLIST元素名称
属性名称 属性类型 属性约束
属性名称 属性类型 属性约束
>
u 属性类型
CDATA 字符串
枚举(没有提供关键字) 只能从枚举列表中中任选其一,如(男人|女人)
ID 代表唯一的值,不能只写数字
u 属性的约束
#REQUIRED 必须出现的
#IMPLIED 可选的
#FIXED 固定值 #FIXED "值"
直接值 表示该属性的默认取值
(5)实体定义
实体:为一段内容创建一个别名,以后在XML文档中就可以使用别名引用这段内容了
<!ENTITY别名名称 "实体内容" >
在DTD中定义,在XML中引用引用方式:&别名;
DTD中定义:
<!ENTITY copyright “版权所有”>
XML中引用:
©right;
(6)再来个例子
<?xmlversion = "1.0" encoding="GB2312" ?>
<!DOCTYPE联系人列表[
<!ELEMENT 联系人列表 ANY>
<!ELEMENT 联系人(姓名,EMAIL)>
<!ELEMENT 姓名(#PCDATA)>
<!ELEMENT EMAIL(#PCDATA)>
<!ATTLIST 联系人 编号 ID #REQUIRED>
<!ENTITY copyright “版权所有”>
]>
<联系人列表>
<联系人 编号=“p1">
<姓名>张三</姓名>
<EMAIL>zhang@it315</EMAIL>
</联系人>
<联系人 编号=“p2">
<姓名>李四</姓名>
<EMAIL>li@it315</EMAIL>
</联系人>
</联系人列表>
©right;
3.2 schema
(1)与DTD的对比:
u schema本身也是XML文档,符合XML的语法结构,
u DOM、SAX等XML API都可以解析schema文档
u schema对名称空间支持的好
u schem支持更多的数据类型,包括自定义的数据类型
(2)概述
u 有预先定义的元素和属性
u 后缀名是.xsd
u 只能有一个根节点,名称是schema
u 关于名称空间
编写完schema文档时,用唯一的URI来表示名称空间;
在编写XML文档时,通过xmlns来引入名称空间,通过名称空间引入schema
(3)开发步骤:
u 开发schema的约束文档
引入W3C的名称:在根节点上:xmlns="http://www.w3/2001/XMLSchema"
起名:目标名称空间,值任意targetNamespace=”http://www.itcast/1110”
元素质量:elementFormDefault:qualified(质量好的,用!)、unqualified(质量差的)
定义元素:<element name="书架"></element>
判断简单元素还是复杂元素
如果是简单 在element有属性:type="数据的类型"
如果是复杂 声明标签是复杂的元素:<complexType>
说明子元素之间的关系:<sequence>
u 开发XML实例文档
引入W3C名称空间:xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
引入自己编写的schema的名称空间:xmlns="http://www.itcast/1110"
引入schema,通过名称空间:xsi:schemaLocation="http://www.itcast/1110 book.xsd"
问题:元素上不能有相同的属性名称所以引入时可以通过起别名的方式:aa。技巧是,在下面出现标签的概率小起别名。如,schemaLocation属性是W3C提供的,如果W3C名称空间要是有别名的话,先把别名写上:xsi:schemaLocation="名称空间 schema文件的地址"
(4)小案例
约束文档:book.xsd
<?xmlversion="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3/2001/XMLSchema"
targetNamespace="http://www.itcast/1110"
elementFormDefault="qualified">
<element name="书架">
<complexType>
<sequencemaxOccurs="unbounded">
<elementname="书">
<complexType>
<sequence>
<elementname="书名" type="string"></element>
<elementname="作者" type="string"></element>
<elementname="售价" type="double"></element>
<elementname="简介" type="string"></element>
</sequence>
<!--书的属性 -->
<attributename="出版社" type="string" ></attribute>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>
实例文档:book.xml
<?xmlversion="1.0" encoding="UTF-8"standalone="no"?>
<书架 xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns="http://www.itcast/1110"
xsi:schemaLocation="http://www.itcast/1110book.xsd">
<书 出版社="清华出版社">
<书名>javaweb开发大全</书名>
<作者>班长</作者>
<售价>99.8</售价>
<简介>这是不错啊</简介>
</书>
<书>
<书名>葵花宝典</书名>
<作者>岳不群</作者>
<售价>99.8两</售价>
<简介>欲练此功...</简介>
</书>
</书架>
4.解析
(1)解析技术
u DOM解析:在内存中形成树状结构。优点是方便做增删改的操作;缺点是如果文档过大,容易产生内存溢出的问题
u SAX解析:基于事件驱动,边读边解析。优点是不会产生内存溢出问题;缺点是不能做增删改操作。但是DOM4J在内存生成树状结构
(2)XML解析开发包
u JAXP:是SUN公司推出的解析标准实现
u Dom4J:是开源组织推出的解析开发包
u JDom:是开源组织推出的解析开发包
4.1 JAXP—DOM
解析器工厂类:DocumentBuilderFactory
解析器对象:DocumentBuilder
(1)解析方法:写在.java文件中
获取解析器工厂类:DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
获取解析器对象:DocumentBuilder builder = factory.newDocumentBuilder();
解析XML的文档,返回document对象:Document document = builder.parse("src/book2.xml");
(2)回写:当删减子节点时需要回写操作
创建回写类的工厂:TransformerFactory tfFactory = TransformerFactory.newInstance();
获取回写类:Transformer tf = tfFactory.newTransformer();
调用回写的方法:tf.transform(new DOMSource(document), newStreamResult("src/book2.xml"));
(3)案例
获取作者文本内容
在第二本末尾添加子节点
删除节点
4.2 JAXP—SAX
只能做查询,不能做增删改。
(1)组成
解析器:将自己编写的事件处理器传入
获取解析器的工厂:SAXParserFactoryfactory = SAXParserFactory.newInstance();
获取解析器对象:SAXParserparser = factory.newSAXParser();
解析XML(XML文件地址,事件处理器):parser.parse("src/book2.xml",new MyHandler());
事件处理器:自己编写的类,需要继承DefalutHandler类,重写三个方法。
startElement()
characters()
endElement()
(2)解析原理:
解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,就会去调用事件处理器的一个方法,把当前解析到的xml文件内容作为方法的参数传递给事件处理器。事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理!
(3)案例
编写事件处理器,只打印作者的内容,并可以指定获取哪一个作者
classMyHandler2 extends DefaultHandler{
private boolean flag = false;//设置标记来指定需要打印的指定标签文本内容
private int count = 0;//设置计数器来指定需要打印的那个指定标签文本内容
public void startElement(Stringuri, String localName, String qName,
Attributes attributes)throws SAXException {
if("作者".equals(qName)){
flag = true;
count++;}}
publicvoid characters(char[] ch, int start, int length)throws SAXException {
if(flag && count ==1){
String str = newString(ch,start,length);
System.out.println(str);}}
publicvoid endElement(String uri, String localName, String qName)throwsSAXException {
flag = false;}
}
4.3 DOM4J
(1)操作节点对象
u 获取文档的根节点:Elementroot = document.getRootElement();
u 取得某个节点的子节点:Elementelement=node.element(“书名");
u 设置节点文字:element.setText("29");
u 取得节点的文字:Stringtext=node.getText();
u 对某节点下的所有子节点进行遍历.
for(Iteratorit=root.elementIterator();it.hasNext();){
Elementelement = (Element) it.next();
//do something }
u 取得某节点下所有名为“member”的子节点,并进行遍历:
Listnodes = rootElm.elements("member");
for (Iterator it = nodes.iterator();it.hasNext();) {
Element elm =(Element) it.next();
// do something}
u 在某节点下添加子节点:ElementageElm = newMemberElm.addElement("age");
u 添加CDATA节点
ElementcontentElm = infoElm.addElement("content");
contentElm.addCDATA(diary.getContent());
u 删除childElm节点:parentElm.remove(childElm);
(2)操作节点对象的属性
u 遍历某节点的所有属性
Elementroot=document.getRootElement();
for(Iterator it=root.attributeIterator();it.hasNext();){
Attributeattribute = (Attribute) it.next();
Stringtext=attribute.getText();
System.out.println(text);}
u 取得某节点下的某属性:
Elementroot=document.getRootElement();
Attributeattribute=root.attribute("size");
u 取得属性的文字: Stringtext=attribute.getText();
u 删除某属性
Attributeattribute=root.attribute("size");
root.remove(attribute);
u 设置某节点的属性和文字:newMemberElm.addAttribute("name","sitinspring");
u 设置属性的文字:
Attributeattribute=root.attribute("name");
attribute.setText("sitinspring");
(3)回写:将文档写入XML
OutputFormatformat = OutputFormat.createPrettyPrint();//设置漂亮的格式
//format.setEncoding("GBK"); // 指定XML编码
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));
writer.write(document);
writer.close();
(4)Dom4j在指定位置插入节点
Elementaaa = DocumentHelper.createElement("aaa");
aaa.setText("aaa");
List list= root.element("书").elements();
list.add(1,aaa);
(5)字符串与XML转换
//将字符串转化为XML
Stringtext = "<members><member>sitinspring</member></members>";
Document document = DocumentHelper.parseText(text);
//将文档或节点的XML转化为字符串
SAXReaderreader = new SAXReader();
Document document = reader.read(newFile("input.xml"));
Element root=document.getRootElement();
String docXmlText=document.asXML();
StringrootXmlText=root.asXML();
Element memberElm=root.element("member");
String memberXmlText=memberElm.asXML();
(6)DOM4J对XPATH的支持
使用前和DOM4J一样,要导入!只有两种使用方式:
selectNodes("参数") 返回一个list集合
selectSingleNode(("参数") 返回一个Node对象
参数就是xpath的语法
/AAA/BBB 按照层级关系,获取BBB的节点
//BBB 无论层级关系,找到BBB的节点
* 代表是所有
/AAA/BBB[1] 获取BBB所有标签的第一个
/AAA/BBB[last()] 获取BBB所有标签的最后一个
@ 属性
(7)案例:
/**对XPATH的支持*/
public static void run6() throwsException{
// 获取解析器对象
SAXReader reader = newSAXReader();
// 解析XML
Document document =reader.read("src/book2.xml");
// List<Node> list =document.selectNodes("/书架/书/作者");
List<Node> list =document.selectNodes("//作者");
Node author2 = list.get(1);
System.out.println(author2.getText());
}
/** 修改文本内容 */
public static void run5() throwsException{
// 获取解析器对象
SAXReader reader = newSAXReader();
// 解析XML
Document document =reader.read("src/book2.xml");
// 获取根节点
Element root =document.getRootElement();
// 获取狗的节点
Element book2 = (Element)root.elements("书").get(1);
Element dog = book2.element("狗");
dog.setText("小狗");
// 回写
OutputFormat format =OutputFormat.createPrettyPrint();
XMLWriter writer = newXMLWriter(new FileOutputStream("src/book2.xml"),format);
writer.write(document);
writer.close();
}
/*删除第二本书下的猫节点*/
public static void run4() throwsException{
// 获取解析器对象
SAXReader reader = newSAXReader();
// 解析XML
Document document =reader.read("src/book2.xml");
// 获取根节点
Element root =document.getRootElement();
// 获取猫
Element book2 = (Element)root.elements("书").get(1);
Element cat =book2.element("猫");
// 通过猫获取猫的父节点
// cat.getParent();
// 通过父节点删除猫
book2.remove(cat);
// 回写
OutputFormat format =OutputFormat.createPrettyPrint();
XMLWriter writer = newXMLWriter(new FileOutputStream("src/book2.xml"),format);
writer.write(document);
writer.close();
}
/**
* 在第二本书的作者标签之前添加团购价的标签
* @throws Exception
*/
public static void run3() throwsException{
// List
// 获取解析器对象
SAXReader reader = newSAXReader();
// 解析XML
Document document = reader.read("src/book2.xml");
// 获取根节点
Element root =document.getRootElement();
// 获取第二本书
Element book2 = (Element)root.elements("书").get(1);
// 获取书下的所有子节点,返回List集合
List<Element> list =book2.elements();
// 创建元素对象 DocumentHelper.createElement("狗")
Element dog =DocumentHelper.createElement("狗");
dog.setText("大狗");
// list.add(index,Element);
list.add(1, dog);
OutputFormat format =OutputFormat.createPrettyPrint();
// 回写
XMLWriter writer = new XMLWriter(newFileOutputStream("src/book2.xml"),format);
writer.write(document);
writer.close();
}
/**
* 在第二本书下添加子节点
*/
public static void run2() throwsException{
// 获取解析器对象
SAXReader reader = newSAXReader();
// 解析XML,返回Document对象
Document document =reader.read("src/book2.xml");
// 获取根节点
Element root =document.getRootElement();
// 获取第二本书
Element book2 = (Element)root.elements("书").get(1);
// 可以直接在第二本书下添加子节点,设置文本内容
book2.addElement("猫").setText("我是猫");
// 回写类
XMLWriter writer = newXMLWriter(new FileOutputStream("src/book2.xml"),format);
// 回写了文档对象
writer.write(document);
// 关闭流
writer.close();
}
/**
* 获取第二本书作者的文本内容
* @throws Exception
*/
public static void run1() throwsException{
// 获取解析器对象
SAXReader reader = newSAXReader();
// 解析XML,返回Document对象
Document document =reader.read("src/book2.xml");
// 获取根节点(书架标签)
Element root =document.getRootElement();
// 获取书的节点,获取第二本书
List<Element> books =root.elements("书");
Element book2 = books.get(1);
// 获取作者的标签
Element author2 =book2.element("作者");
// 获取文本内容
System.out.println(author2.getText());
}
JavaWEB服务器
1.软件开发的两种架构:
C/S:client/server 客户端/服务器端。 交互性好,服务器压力小。但客户端更新后需要下载B/S:browser/server 浏览器/服务器端。 服务器压力,更新时服务器更新就好
2.Web的两种资源
静态web资源:指web页面中供人们浏览的数据始终是不变 Html
动态web资源:数据是由程序产生的,实时变化着 JSP/Servlet、ASP、PHP、python
3.服务器
(1)原理:网络编程
(2)概念:
硬件:一台电脑
软件:需要在这台电脑上安装服务器的软件
Ø 如果一台电脑上安装了服务器的软件的话,那么这台电脑就可称为WEB服务器
(3)访问:http://+IP+端口号 默认端口号是80:可以不写!
(4)常见服务器:WebLogic、WebSphere、Tomcat (apache提供,只提供Servlet/JSP规范)
注:JAVAEE所有的规范:EJB、servlet/jsp规范
4.Tomcat
(1)目录结构
bin 存放启动项,关闭项
conf 存放Tomcat配置文件
lib 服务器运行使用的jar包
logs 日志文件,运行时产生的日志。
temp 运行时临时文件
webapps 发布 web 应用程序时,通常把 web应用程序的目录及文件放到这个目录下work JSP翻译成Servlet程序
(2)发布动态的WEB资源程序到webapps目录下,有固定的目录结构!
(3)虚拟路径:即访问的路径,默认和项目名称是相同(不要去修改)。因此,直接复制项目时,需要修改虚拟路径:项目上右键 -- 选择属性 -- MyEclipse -- web -- 修改虚拟路径
5.web通信原理
HTTP协议
1.概述
n HTTP的协议:超文本传输协议
n 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式,是应用层协议
n 基于TCP连接的传输协议
n 基于请求-响应模式的协议:先请求后响应
n 默认端口是80
n 安装IE浏览器插件HttpWatch,可以查看IE浏览器通过HTTP协议获取某个页面
n 版本:HTTP/1.0、HTTP/1.1
HTTP/1.0:链接后,只能获取一个web资源;链接后,发送请求,服务器做出响应后链接立即断开。
HTTP/1.1:链接后,可以获取多个web资源;链接后,发送请求,服务器做出响应后链接不会立即断开,再次发送请求,直接有一段时间没操作,自动断开。
2.HTTP请求
n 常用的请求头
Accept:text/html,image/* 客户端识别文件类型
Accept-Charset:ISO-8859-1 客户端字符集
Accept-Encoding:gzip 客户端支持编码类型gzip 压缩格式
Accept-Language:zh-cn 客户端语言
Host:www.itcast:80 访问服务器地址
If-Modified-Since:Tue, 11 Jul 2000 18:23:51 GMT 和Last-Modified一起使用 控制缓存
Referer:http://www.itcast/index.jsp 代表当前网页的来源
User-Agent:Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) 浏览器类型
Connection:close/Keep-Alive 请求后连接是关闭还是保持
Date:Tue, 11 Jul 2000 18:23:51 GMT 请求时间
n 重要的请求头
If-Modified-Since 必须和响应头信息一起来完成控制本地的缓存。
Referer 当前的网页的来源。(防止盗取链接)
User-Agent 判断浏览器的版本(文件下载时,不同浏览器进行不同处理)
防盗链
//防盗链
Stringreferer = request.getHeader("referer");
if(referer!=null&& referer.equals("http://localhost:8080/day03/index.html")){
// 有效
response.setContentType("text/html;charset=gbk");
response.getWriter().println("笔记本1000元");
}else{
// 无效
response.setContentType("text/html;charset=gbk");
response.getWriter().println("盗链真无耻!");
}
//URL伪造
URL url =new URL("http://localhost:8080/day03/referer");
URLConnectionconn =url.openConnection();
conn.setRequestProperty("referer","http://localhost/day03/index.html");
byte[]buf = new byte[1024];
InputStreamis = conn.getInputStream();
int len =is.read(buf);
System.out.println(newString(buf,0,len));
3.HTTP响应
(1)请求行
n 常用的状态码
200 :请求成功处理
302 :请求重定向
304 :服务器端资源没有改动,通知客户端查找本地缓存
404 :客户端访问资源不存在
500 :服务器内部出错
(2)请求头
n 常用的响应头
Location:http://www.it315/index.jsp 结合302完成重定向操作Location重定向后地址
Server:apachetomcat 服务器类型
Content-Encoding:gzip 响应编码类型 gzip压缩
Content-Length:80 响应长度
Content-Language:zh-cn 响应语言
Content-Type:text/html; charset=GB2312 响应字符集
Last-Modified:Tue, 11 Jul 2000 18:23:51 GMT 和If-Modified-Since 一起使用,实现服务器缓存
Refresh:1;url=http://www.it315 页面自动刷新
Content-Disposition:attachment; filename=aaa.zip 文件下载
n 重要的响应头
Location 和302一起完成重定向。
Last-Modified 和请求头If-Modified-Since一起控制缓存。和状态码304
Refresh 完成页面的定时跳转
Content-Disposition 设置文件是以附件打开
禁用缓存
Expires:-1
Cache-Control:no-cache
Pragma:no-cache
(3)转发与重定向
转发:找班长借钱,班长自己再找副班长借钱,给我
重定向:找班长借钱,发送一次请求,回了我没钱,返回状态码302,给出副班长地址,再去找副班长借钱,又发送了一次
JDBC
1.jdbc简介
JDBC:Java Data Base Connectivity,java数据库连接,简单地说,就是直接通过java语言去操作数据库。jdbc是一套标准,它是由一些接口与类组成的。
组成jdbc的两个包:
(1)java.sql
类:DriverManger
接口:Connection Statement ResultSet PreparedStatement
CallableStatement(用于调用存储过程)
(2)javax.sql
接口 DataSource
2.第一个jdbc程序
// 注册驱动
DriverManager.registerDriver(newDriver());
// 获取连接对象
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/day17","root", "abc");
// 通过连接对象获取操作sql语句Statement
Statementst = con.createStatement();
// 操作sql语句
Stringsql = "select * from user";
// 操作sql语句(select语句),会得到一个ResultSet结果集
ResultSetrs = st.executeQuery(sql);
// 遍历结果集
while(rs.next()){
int id=rs.getInt("id");
Stringusername=rs.getString("username");
Stringpassword=rs.getString("password");
Stringemail=rs.getString("email");
System.out.println(id+" "+username+" "+password+" "+email);}
//释放资源
rs.close();
st.close();
con.close();
ü Get:基本思路:装载驱动程序→建立连接→操作数据→释放资源
3.Jdbc操作详解
3.1 DriverManager
加载驱动,并创建与数据库的链接
注册驱动:DriverManager.registDriver(newDriver());
获取连接对象:Connectioncon=DriverManager.getConection(String url,String user,String password);
(1)DriverManager类:是java.sql包下的一个驱动管理的工具类,可理解成是一个容器(Vector),里面可以装入很多数据库驱动
(2)registDriver方法分析
publicstatic synchronized void registerDriver(java.sql.Driver driver)
里面传递的参数是:java.sql.Driver;而我们传递的参数是:com.mysql.jdbc.Driver;
在com.mysql.jdbc.Driver类中有一段静态代码块:
static{
try{java.sql.DriverManager.registerDriver(new Driver());
}catch (SQLException E) {
thrownew RuntimeException("Can't register driver!");}
}
对于上面的代码就存在问题:在驱动管理器中会装入两个mysql驱动.
解决方案是:使用反射 Class.forName("com.mysql.jdbc.Driver");
分析:使用反射的方式来加载驱动有什么好处?
只加载一次,装入一个驱动对象;降低耦合,不依赖于驱动.
(3)url:用于确定使用哪一个驱动。不同数据库使用的驱动不一样
格式:主协议 子协议 主机 端口 数据库
mysql url: jdbc:mysql://localhsot:3306/数据库名
oralce url: jdbc:oracle:thin:@localhost:1521:sid
mysql的url可以简写成:jdbc:mysql:///数据库名 前提:主机是localhost端口是3306
3.2 Connection
java.sql.Connection,代表的是一个连接对象。简单说,就是我们程序与数据库连接。
获取操作sql的Statement对象: Statement st=con.createStatement();
获取执行存储过程的CallableStatement对象:CallableStatement prepareCall(String sql)
获取执行预处理的PreparedStatement对象: prepareStatement(String sql)
操作事务
setAutoCommit(booleanflag);开启事务
rollback();事务回滚
commit();事务提交
3.3 Statement
java.sql.Statement用于执行sql语句和进行批处理操作
(1)执行sql
DML:insert update delete
intexecuteUpdate(String sql);根据返回值判断非0来确定sql语句是否执行成功
DQL:select
ResultSetexecuteQuery(String sql);
通过execute方法(向数据库发送任意sql语句)来执行任何sql语句:execute(Stringsql)
(2)批处理操作
addBatch(String sql); 将sql语句添加到批处理
executeBatch(); 批量执行
clearBatch(); 清空批处理.
3.4 ResultSet
java.sql.ResultSet它是用于封装select语句执行后查询的结果。
(1)next()方法
publicboolean next();判断是否有下一条记录。如果有返回true,并让游标向下移动一行
(2)getXxx()方法 :获取当前游标指向的这条记录中的列数据。
getInt()、getString()、getDate()、getDouble()…
参数有两种:getInt(intcolumnIndex);或者getInt(String columnName);
如果列的类型不知道,可以通过下面的方法来操作
getObject(intcolumnIndex);或者getObject(String columnName);
3.5关闭资源
Jdbc程序运行完后,切记要释放程序在运行过程中所创建的那些与数据库进行交互的对象,这些对象通常是Connection, Statement和ResultSet对象。特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
再写个带异常处理的jdbc程序
Connectioncon = null;
Statementst = null;
ResultSetrs = null;
try {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
con =DriverManager.getConnection("jdbc:mysql:///day17","root","abc");
// 3.获取操作sql语句对象Statement
st = con.createStatement();
// 4.执行sql
rs = st.executeQuery("select *from user");
// 5.遍历结果集
while (rs.next()) {
int id =rs.getInt("id");
String username =rs.getString("username");
String password =rs.getString("password");
String email = rs.getString("email");
System.out.println(id +" " + username + " " + password+ " " + email);}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6.释放资源
try {
if (rs != null) {
rs.close();}
} catch(SQLException e) {
e.printStackTrace();}
try {
if (st != null)
st.close();
} catch(SQLException e) {
e.printStackTrace();}
try {
if (con != null)
con.close();
} catch(SQLException e) {
e.printStackTrace();}
}
4.封装代码:JdbcUtils
将关于连接数据库的信息定义到配置文件中,读取配置文件进行加载.
public class JdbcUtils {
private staticfinal String DRIVERCLASS;
privatestatic final String URL;
privatestatic final String USERNAME;
privatestatic final String PASSWORD;
static {
DRIVERCLASS=ResourceBundle.getBundle("jdbc").getString("driverClass");
URL= ResourceBundle.getBundle("jdbc").getString("url");
USERNAME= ResourceBundle.getBundle("jdbc").getString("username");
PASSWORD= ResourceBundle.getBundle("jdbc").getString("password");}
static {
try{
// 将加载驱动操作,放置在静态代码块中.这样就保证了只加载一次.
Class.forName(DRIVERCLASS);
}catch (ClassNotFoundException e) {
e.printStackTrace();}
}
public staticConnection getConnection() throws SQLException {
Connection con = DriverManager.getConnection(URL, USERNAME,PASSWORD);
return con;}
}
5.滚动结果集:
默认结果集:只能向下执行,并且只能迭代一次
Statement st=con.createStatement();
ResultSet rs=st.executeQuery(sql);
滚动结果集:可以向上、向下或者直接跳转
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(sql);
简单说,就是在创建Statement对象时,不使用createStatement();而使用带参数的createStatement(int,int)
StatementcreateStatement(int resultSetType,int resultSetConcurrency)
更多推荐
超详细的java基础知识学习(java SE、javaEE)笔记 核心重点!
发布评论