Java知识点总结
第一章:入门
java特点:面向对象,两个基本概念:类、对象,三大特性:封装、继承、多态
java工作方式:1、编写源程序 2、编译源程序 3、运行
Java程序的结构:如果源文件包含一个public类,则文件名必须按该类名命名
第二章:语法基础
引用数据类型变量作为参数则是按引用传递的,包括类,接口和数组
变量的命名规则:首字符:1、字母 2、下划线‘_’ 3、‘$’ 符号
其余部分:任意多的:1、数字 2、字母 3、下划线‘_’ 4、‘$’ 符号
在Java语言中,boolean类型只有“true”和“false”两个值
&和|会将式子两边的运算式都进行运算,但&&会在左边未满足时直接跳过,||会在左边满足时直接跳过
Java 语言中除了单目运算符、赋值运算符和三目运算符是从右向左结合之外,其他运算符均是从左向右结合
数组的声明,分配空间和赋值
int []a;
a=new int[5];
int[] a=new int[5];//可以合一起
int[] score=new int[]{89,79,76}; //不能声明数组长度
//二维数组
int mat[][] = new int [3][4];
int [][] mat = new int [3][];
int mat[][] = { {1,2,3},{4,5,6} };
第三章:类的初步认识
方法之间允许相互调用
第四章:构造函数和函数的重载
只有构造函数才能调用构造函数
第五章:静态和代码块
静态变量
static是一个修饰符,用于修饰成员
static优先于对象存在,因为static的成员随着类的加载就已经存在
静态变量数据存储在方法区的静态区,故亦称对象的共享数据
静态函数
静态代码块
只在类加载时运行一次,且优先于各种其他代码块以及构造函数
静态代码块不能访问普通变量
构造代码块
创建对象时被调用,每次创建时都会被调用
依托于类构造函数,优先于类构造函数执行
存在多个构造代码块,则按书写顺序依次执行
和构造函数的作用类似,通常用于对对象进行初始化,且只要创建对象,构造代码块都会执行一次
构造函数则不一定每个对象建立时都执行
执行顺序:
静态代码块 > 构造代码块 > 构造函数 > 普通代码块
第六章:包和类中类
1.包的创建
package com.hz.classandobject;//若有包的声明,一定作为Java源代码的第一条语句
//包名由小写字母组成,不能以圆点开头或结尾
2.包的使用
package org.it315; //声明包
import org.it315.example.*; //导入包中的所有类
import org.it315.example.Test;//导入包中的指定类
public class TestPackage{
public static void main(String[] args){
new Test().print();
}
}
如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句
如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入
3.访问控制修饰符
4.类中类(我为什么觉得类中类没那么重要)
(1)成员内部类
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
class Outter {
private Inner inner = null;
public Outter() { ... ... }
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() { ... ... }
}
}
public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner();
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
内部类可以拥有private、protected、public及包访问权限。
(2)局部内部类
定义在方法或作用域内的类,仅限方法或该作用域内访问
class People{
public People() { ... ... }
}
class Man{
public Man(){ ... ... }
public People getWoman(){
class Woman extends People{
int age =0;
}
return new Woman();
}
}
(3)匿名内部类
匿名内部类也就是没有名字的内部类。
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
匿名内部类不能定义任何静态成员、方法;
匿名内部类中的方法不能是抽象的;
匿名内部类必须实现接口或抽象父类的所有抽象方法;
匿名内部类没有类名,因此不能定义构造器;java
当匿名内部类和外部类有同名变量(方法)时,默认访问的是匿名内部类的变量(方法),要访问外部类的变量(方法)则需要加上外部类的类名。
(4)静态内部类
由static关键字修饰的内部类。
public class Person{
private String name; //姓名
private Home home; //家庭
public static class Home{
private String address; //家庭地址
private String tel; //家庭电话
}
}
public static void main(String[] args) {
Home home = new Person.Home("上海", "021");
Person p1 = new Person("张三");
Person p2 = new Person("李四");
p1.setHome(home);
p2.setHome(home);
//创建一个静态内部类home对象,p1和p2共享同一个home对象
}
类似于静态成员变量,静态内部类是不需要依赖于外部类;
可定义匿名代码块、静态代码块、静态或非静态成员;
不能在静态内部类中写抽象方法
静态内部类可以直接调用外部类的静态成员,也可通过创建外部类实例的方式调用外部类的非静态成员
(5)作用
public interface InnerInterface {
void innerMethod();
}
class OuterClass {
private class InnerClass implements InnerInterface {
public void innerMethod() {
System.out.println(“实现内部类隐藏");
}
}
public InnerInterface getInner() {
return new InnerClass();
}
}
public class Test {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
InnerInterface inner = outerClass.getInner();
inner.innerMethod();
}
}
//仅能知道OuterClass的getInner()能返回一个InnerInterface接口实例却无从知晓其实现方式;同时由于InnerClass是私有的,因此,连其具体类名都隐藏了起来。
public class Parents1 {
public String name(){
return “inner”;
}
}
public class Parents2 {
public int age(){
return 18;
}
}
Public class Test {
private class Inner1 extends Parents1 {
... ...
}
private class Inner2 extends Parents2 {
... ...
}
}
//通过两个内部类间接实现了Test类对Parents1和Parents2的多重继承
public class DemoTest extends MyDemo {
private class inner implements Demo {
public void test() {
System.out.println("接口的test方法");
}
}
public Demo getIn() {
return new inner();
}
public static void main(String[] args) {
DemoTest dt = new DemoTest();
Demo d = dt.getIn();
d.test();//调用接口而来的test( )方法
dt.test();//调用继承而来的test( )方法
}
}
第七章:继承
Java类的继承结构为树状结构(即层次结构),Java系统类库中的java.lang.Object类为整个树状结构类图的、最顶层的树根节点
同名属性不能覆盖,只会在子类中增加新的属性
继承后的初始化顺序: 父类属性 \(\rightarrow\) 父类构造函数 \(\rightarrow\) 子类属性 \(\rightarrow\) 子类构造函数
在Java类中使用super关键字来调用父类中的指定操作,super可用于在子类构造函数中调用父类的构造函数,当子父类出现同名成员时,可用super表明调用的是父类成员
子类将先执行继承自父类的无参数构造函数,然后再执行自己的构造函数
在有参数构造函数中首行必须用super(参数)语句,实现对基类数据成员初始化的任务
重写
在子类中可以根据需要对从父类中继承来的方法的实现过程进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
保持与父类完全相同的方法头部声明,即应与父亲有完全相同的方法名和参数列表
子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
声明为private和final的方法不能被重写
声明为 static 的方法不能被重写,但可被再次声明
第八章:多态
1.JAVA中的多态性
编译时多态:主要指方法的重载,它根据参数列表的不同来区分不同的方法。通过编译后变成不同的方法。
运行时多态:即动态绑定,指在执行期间(非编译期间)判断引用对象的实际类型,根据实际类型判断并调用相应的属性和方法。主要用于继承父类和实现接口时,父类引用指向子类对象
多态存在的三个必要条件:继承,重写,父类引用指向子类对象
当使用多态方式调用方法时,首先检查父类中是否有该方法,若无,则编译错误;若有,则调用父类同名同参数的方法,但实际执行的是子类重写父类方法,称为虚拟方法调用
静态方法并不具有多态性
2.多态的转型
向上转型:父类类型 变量名 = new 子类类型();
向下转型:子类类型 变量名 =(子类类型) 父类类型的变量;(一般用于需要使用子类特有功能时)
class Fruit{
public void myName(){
System.out.println("我是父类 水果...");
}
}
class Apple extends Fruit{
public void myName(){
System.out.println("我是子类 苹果...");
}
public void myMore(){
System.out.println("我是你的小呀小苹果~~~~~~");
}
}
public class Test{
public static void main(String[] args) {
Fruit a=new Apple();//向上转型
a.myName();
Apple aa=(Apple)a;//强制转换时要保证父类变量是对子类对象的引用
aa.myName();
aa.myMore();//子类的特有功能
Fruit f=new Fruit();
Apple aaa=(Apple)f;//这就不行,编译通过,但是运行会报错
aaa.myName();
aaa.myMore();
if(f instanceof Apple){//对象x instanceof 类A,判断某个对象是否属于某种数据类型
Apple aaa=(Apple)f;
aaa.myName();
aaa.myMore();
}
}
}
第九章:抽象类,final和接口
1.抽象类
定义:类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
用abstract关键字来修饰一个类,这个类叫抽象类,用abstract来修饰一个方法,该方法叫抽象方法。
public abstract class Shape {
public int width; // 几何图形的长
public int height; // 几何图形的宽
public Shape(int width, int height) {
this.width = width;
this.height = height;
}
public abstract double area(); // 定义抽象方法,计算面积
}
拥有一个抽象方法并不是构建一个抽象类充分必要条件
抽象和私有是冲突的,抽象和静态是冲突的,抽象和final也是如此
抽象类可以有构造函数,初始化抽象类的成员,但构造函数不可用abstract修饰。
abstract也不可用来修饰变量、代码块。
2.final
final标记的类不能被继承 - String类、System类、StringBuffer类
final标记的方法不能被子类重写 - 比如:Object类中的getClass()
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次,必须在定义时或者构造器中进行初始化赋值
final修饰局部变量 ,则该变量变成了常量
final修饰方法的形式参数 ,则该形参数在方法中不能修改,是常量
static final修饰属性:全局常量
对于一个final变量,如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,但是它指向的对象的内容是可变的。
当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用
static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变(和c++中const相似)
3.接口
接口比抽象类更加的抽象化
“接口把方法的特征和方法的实现分隔开来,这种分隔体现在接口常常代表一个角色,它包装与该角色相关的操作和属性,而实现这个接口的类便是扮演这个角色的演员。一个角色由不同的演员来演,而不同的演员之间除了扮演一个共同的角色之外,并不要求其他的共同之处”
[访问修饰符] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
1)特点
所有成员变量都默认是public static final修饰(全局常量),必须在定义时进行初始化
所有抽象方法都默认是由public abstract修饰
没有构造函数
支持多继承机制
2)实现
接口中的方法只能由实现接口的类来实现,除非实现接口的类是抽象类,否则该类要定义接口中的所有方法
一个类可以实现多个接口,接口也可以继承其它接口。与继承关系类似,接口与实现类之间存在多态性
无论何时实现一个由接口定义的方法,它都必须实现为 public,因为接口中的所有成员都显式声明为 public
4.Object类
如果在类的声明中未使用extends关键字显式指明其父类,则默认父类为java.lang.Object类
1)Object类的主要方法
protected Object clone()
被克隆的对象需要实现Cloneable接口
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();//创建了一个新的对象,二者具有不同的地址
//clone是浅拷贝的
String toString()
返回一个字符串,该字符串由类名(对象是该类的一个实例)、标记符“@”和此对象哈希码的无符号十六进制表示组成
public class Test1 {
public static void main(String[] args){
Object o1 = new Object();
System.out.println(o1.toString());
}
}
//输出 java.lang.Object@7852e922
System.out.println(“now=”+now);//在进行String与其它类型数据的连接操作时,会自动调用toString()方法
System.out.println(“now=”+now.toString());//和上面等价
可以根据需要在用户自定义类型中重写toString()方法:String类重写了toString()方法,返回字符串的值
boolean equals(Object obj)
用来判断两个对象引用的是否是同一对象
public boolean equals(Object obj) {
return (this == obj);
}
int x [] = {1, 2, 3};
int y [] = {1, 2, 3};
System.out.printf("x.equals(y): ", x.equals(y));//如何比较两个数组的内容
Integer x[] = { 1, 2, 3};
Integer y[] = { 1, 2, 3};
System.out.println("x.equals(y):", Arrays.deepEquals(x, y));
//deepEquals()方法要求传入的数组元素必须是对象
(==运算符
对于基本类型执行值的比较。即,只要两个变量的值相等,则为true,类型可以不同。
对于引用类型执行引用比较(是否指向同一个对象)。即,只有指向同一个对象时,==才返回true)
equals()应用于引用数据类型,其作用与“==”相同
类File、String、Date及包装类等的equals()方法都被重写过,是比较类型及内容
5.包装类
//基本数据类型包装成包装类的实例--装箱
//通过包装类的构造器实现:
int i= 500; Integer t = new Integer(i);
//还可以通过字符串参数构造包装类对象:
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException
Boolean bobj = new Boolean(“true”);
//获得包装类对象中包装的基本类型变量--拆箱
//.xxxValue()方法:
boolean b = BooleanObject.booleanValue();
//字符串转换成基本数据类型
//通过包装类的构造器实现:
int i= new Integer(“12”);
//通过包装类的parseXxx(String s)静态方法:
float f = Float.parseFloat(“12.1”);
//基本数据类型转换成字符串
//调用字符串重载的valueOf()方法:
String fstr= String.valueOf(2.34f);
//或者String intStr= 5 + “”
Object o1=true?new Integer(1):new Double(2.0);
System.out.println(o1);
//根据 Java 语言规范,在三元运算符中存在自动类型转换的情况下,会将两个操作数中类型更为精确的那个作为结果的类型,因此变量 o1 的类型被推断为 Double,并且输出结果将是 1.0
double a = 2.0;
Double d = 2.0;
System.out.println(a == d);
//在比较两个不同数据类型的值时,Java 会先尝试将其中一个值转换为另一个值的数据类型,然后再进行比较。在这种情况下,编译器会将基本数据类型 double 的值转换为包装类 Double 的值,然后再进行比较
第十章:异常处理
Java通过API中Throwable类的众多子类描述各种不同的异常。Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的错误条件。当条件生成时,错误将引发异常
1.Throwable类
Throwable类是类库java.lang包中的一个类,该类不能直接使用。它派生了两个子类:Exception和Error。
Error类
表示程序无法处理的错误,是运行应用程序中较严重问题,如,Java虚拟机运行错误(Virtual MachineError),JVM内存资源不足(OutOfMemoryError),类定义错误(NoClassDefFoundError)等。
Exception类
Exception指程序员可以用针对性代码处理的不正常,如:编译异常,运行时异常
Exception类有若干子类,每个子类代表了一种特定的错误:
ArithmeticException: 算术异常,如除数为0。
ArrayIndexOutOfBoundsException:数组越界异常。
ArrayStoreException:数组存储异常。
ClassCastException:类型转换异常。
IllegalArgumentException:无效参数异常。
NegativeArraySizeException:数组尺寸为负异常。
NullPointerException:未分配内存异常。
NumberFormatException:数字格式异常。
StringIndexOutOfBoundsException:字符串越界异常。
2.异常的处理
1)异常的就地捕获
try{
//可能会发生的异常
}
catch(异常类型 异常变量名){
//针对异常进行处理的代码
e.printStackTrace//调用异常对象的方法void printStackTrace(),输出异常的堆栈信息。
}[可以有多个catch代码块...]
[finally{
//释放资源代码;
}]
//第1步:用try块将可能出现异常的代码包起来。
//第2步:用catch块来捕获异常并处理异常。
//第3步:如果有一些代码是不管异常是否出现都要运行的,用finally块将其包起来。
一段代码可能会引发多种类型的异常,当引发异常时,会按顺序来查看每个 catch 语句,并执行第一个与异常类型匹配的catch语句,执行其中一条 catch 语句后,其后 catch 语句将被忽略。
finally并非可有可无,finally最大的特点就是:在try块内即使跳出了代码块,甚至跳出函数,finally内的代码仍然能够运行。
System.exit(1); // finally语句块不执行的唯一情况
//System.exit(1)退出Java虚拟机。
try后不能既无catch也无finally,catch不能独立于try存在;
2)向前抛出异常
throws声明异常
public void fun() throws NullPointerException,Exception2,... {
//该函数如出现NullPointerException,则向前抛出
}
//可以同时声明多个异常,之间由逗号隔开。
//如果想要抛出所有类型的异常
public void fun() throws Exception {
//该函数如出现异常,则向前抛出
}
//对于抛出的异常,调用者可以就地处理,也可以再通过thows继续抛出
callFun() throws Exception{
fun();
}
throw手动抛出异常
IOException e=new IOException();
throw e;
//除了系统定义好的异常,用户还可以自己定义异常类型。
class MyExecption extends RuntimeException{
//自定义异常类,继承RuntimeException或Exception
public MyExecption(){ }
public MyExecption(String msg){
super(msg);
}
//定义重载构造函数,其中构造函数初始化异常信息
}
多个catch时,父类的catch要放在下面!
Runtime异常(Unchecked异常):Runtime异常是指在运行时可能出现的异常,它们是RuntimeException类及其子类的子类。与Checked异常不同,Runtime异常不需要在代码中显式捕获或声明,可以选择性地进行处理。Runtime异常通常表示程序的逻辑错误或运行时错误,例如NullPointerException、ArrayIndexOutOfBoundsException等。
十一章&十二章:API
1.Math类
java.lang.Math类用final修饰,所以不能有子类;
private构造方法,不能通过new的方法在其它类中构造Math对象;
所有方法均为static,可供直接调用。
序号 | 方法 | 功能描述 |
---|---|---|
1 | public static double abs(a) | 计算绝对值 |
2 | public static double max/min(a, b) | 返回a,b中的较大/小值 |
3 | static double random() | 返回一个[0.0,1.0)带正号的double值 |
4 | public static long round(double a) | 返回最接近参数的 long |
5 | static double pow(double a, double b) | 返回a的b次幂的值 |
6 | public static double sqrt(double a) | 返回平方根 |
7 | public static double exp(double a) | 返回返回欧拉数e的a次幂 |
8 | static double log(double a) | 返回(底数是e)a的自然对数 |
9 | public static double sin/cos/tan(double a) | 返回弧度值的正弦/余弦/正切值 |
10 | public static double toDegrees(double angrad) | 返回弧度对应的角度值 |
11 | public static double toRadians(double angdeg) | 返回角度对应的弧度值 、 |
12 | public static double ceil(double a) | 返回不小于参数的最小整数的double值 |
13 | public static double floor(double a) | 返回不大于参数的最大整数的double值 |
1.5 Random类
生成Random类对象: public Random()
public boolean nextBoolean() //生成一个随机的boolean值
public double nextDouble() //生成一个[0, 1.0)间的随机double值
public int nextInt() //生成一个随机的int值
public int nextInt(int n) //生成一个[0, n)之间的随机int值
2.String类
String代表字符串,字符串变量属于对象;
java.lang.String是一个final类,也不能有子类;
字符串存储在一个final的char类型字符数组中;
属于常量,用””引起来表示。创建后其值不能更改;
1)构造函数
序号 | 方法 | 功能描述 |
---|---|---|
1 | public String() | 构造一个空字符串对象 |
2 | public String(byte[] bytes) | 通过byte数组构造字符串对象 |
3 | public String(byte[] bytes, int offset, int length) | 通过byte数组,从offset开始,总共length长的字节构造字符串对象 |
4 | public String(char[] value) | 通过char数组构造字符串对象 |
5 | public String(char[] value, int offset, int length) | 通过char数组,从offset开始,总共length长的字节构造字符串对象 |
6 | public String(String original) | 构造一个original的副本 |
7 | public String(StringBuffer buffer) | 通过StringBuffer数组构造字符串对象 |
字符串对象可以不用new!
2)字符串的内存存储实现
字符串重新赋值时,是重新在新内存区赋值,而不是对原有存储区的value数组赋值。
当对现有字符串进行连接操作时,也是重新指定新内存区赋值。
当调用replace方法修改字符串时,也是重新指定新内存区赋值。
总结起来,直接赋值方式创建的字符串对象会放入字符串常量池中,而使用 new
关键字实例化的字符串对象会在堆内存中创建。对于直接赋值方式创建的字符串对象,如果字符串常量池中已经存在相同内容的字符串对象,则会共享同一个对象,否则会创建一个新的对象。因此,在比较字符串对象时,应该使用 equals()
方法来比较它们的内容是否相同,而不是使用 ==
运算符来比较它们的引用是否相同。
3)String类的主要方法
序号 | 方法 | 功能描述 |
---|---|---|
1 | public char charAt(int index) | 返回index所指位置的字符 |
2 | public String concat(String str) | 将两字符串连接 |
3 | public boolean startsWith(String str)/endsWith(String str) | 测试字符串是否以str开始/结尾 |
4 | public boolean equals(Object obj) | 比较两对象 |
5 | public char[] getBytes()/getBytes(String str) | 将字符串转换成字符数组返回 |
6 | public int indexOf(String str)/indexOf(String str, int fromIndex) | 返回字符串在串中位置 |
7 | public int length() | 返回字符串的长度 |
8 | public String replace(char old ,char new) | 将old用new替代 |
9 | public char[] toCharArray () | 将字符串转换成字符数组 |
10 | public String substring(int start)/substring(int start, int end) | 截取字符串内某段并返回 |
11 | public String toLowerCase()/toUpperCase() | 转小写/大写 |
12 | public String trim() | 去掉两边空格 |
13 | public static String valueOf(各种类型) | 将各种类型转为字符串 |
public int compareTo(String anotherString)
该方法是对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
public int compareToIgnoreCase(String anotherString)
与compareTo方法相似,但忽略大小写。
public boolean equals(Object anotherObject)
比较当前字符串和参数字符串,在两个字符串相等的时候返回true,否则返回false。
public boolean equalsIgnoreCase(String anotherString)
与equals方法相似,但忽略大小写。
String s1="hello";
String s2="world";
String s3="hello"+"world";
String s6=(s1+s2).intern();
System.out.println(s3==s6);//true
//调用intern()方法后,JVM 会在当前类的常量池中查找是否存在与str等值的String,若存在则直接返回常量池中相应String的引用;若不存在,则会在常量池中创建一个等值的String,然后返回这个String在常量池中的引用
public boolean contains(String str)
该方法判断参数str是否被包含在字符串中,并返回一个布尔类型的值
public String[] split(String str)
该方法将str作为分隔符进行字符串分解,分解后的字符串在字符串数组中返回。
3.StringBuffer类
提供了String不支持的添加、插入、修改和删除之类的操作
1)构造函数
序号 | 方法 | 功能描述 |
---|---|---|
1 | public StringBuffer() | 构造一个初始容量为 16 个字符的空字符串缓冲区 |
2 | public StringBuffer(int size) | 构造一个指定容量为size个字符的空字符串缓冲区 |
3 | public StringBuffer(String str) | 构造一个初始内容为指定字符串str的字符串缓冲区,初始容量为str长度+16个字符 |
2)主要方法
序号 | 方法 | 功能描述 |
---|---|---|
1 | public StringBuffer append(各种类型) | 返回在字符串末尾追加各种类型的数据后的字符串表示形式 |
2 | public StringBuffer insert(int offset, 各种类型) | 返回在指定位置插入某种类型变量后的字符串表示形式 |
3 | public StringBuffer deleteCharAt(int index) | 返回删除在该序列指定位置的 char后的字符串表示形式 |
4 | public StringBuffer delete(int start, int end) | 返回删除在该序列指定位置段的字符串后的字符串表示形式 |
5 | public int length() | 返回字符串中的字符个数 |
6 | public char charAt(int index) | 返回指定位置index处的字符 |
7 | public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) | 从源序列复制指定起止位置的字符串到目标字符数组dst的指定位置 |
8 | public StringBuffer reverse() | 将字符串倒转 |
9 | public void setCharAt(int index, char ch) | 将序列指定位置的字符设置为ch |
10 | public String toString() | 将该序列转换为String并返回 |
new StringBuffer()得到的序列初始capacity为16;
new StringBuffer(String str)得到的序列capacity是str.length+16;
序列内容发生变化时,若内容长度小于16则默认容器的大小为16。若大于16则会调用expandCapacity 函数将capcity扩展为:旧容量*2+2,以此类推。
StringBuffer bf = new StringBuffer();
System.out.println("bf's capacity is: ” + bf.capacity());//16
StringBuffer bf1 = new StringBuffer("This is a StringBuffer!");
System.out.println("bf1’s capacity is: ” + bf1.capacity());//16+23
bf.append("This is a StringBuffer!");
System.out.println("bf’s capacity is: “ + bf.capacity()); //16*2+2
(第一遍建议看ppt做例题+自己把常用方法敲一敲)
4.System类
该类的构造器是private的,故无法创建该类的对象;
内部成员变量和方法都是static的,可直接调用;
System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
常用方法
native long currentTimeMillis():
该方法用于返回当前计算机时间,时间格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。
void exit(int status)
该方法的作用是退出程序。其中status的值为0代表正常退出。
void gc()
该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况;
lString getProperty(String key)
该方法的作用是获得系统中属性名为key的属性对应的值。
第十三章:多线程
1.基础概念
并发:指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。
程序:是为完成特定任务,用某种语言编写的一组计算机指令的集合,即指一段静态的代码;
进程:指程序的一次执行活动,是一个动态的过程。它是资源申请、调度和独立运行的单位,使用系统中的运行资源,具有生命周期;
线程:进程内部的一个独立执行单元(路径),一个进程可以同时并发运行多个线程。
进程与线程的区别:一个进程至少包含一个线程,同一个进程中的多个线程之间可以并发执行;进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位;
主线程:程序启动时,一个线程立刻运行,该线程通常称为程序的主线程。在Java中,main()就是主线程。其他子线程都是由主线程产生的,主线程通常必须最后完成执行,因其需执行各种关闭动作。
2.多线程的创建
继承java.lang.Thread 类
//定义一个线程类 继承于 java.lang.Thread 类
class Thread1 extends Thread{
// 重写Thread类的run()方法
public void run(){
for(int i=0; i<10; i++){
System.out.println("play music " + i);
}
}
}
public class ThreadDemo1 {
public static void main(String args[]) {
for(int i=0; i<10; i++){
System.out.println("play games " + i);
if(i==3){
//在 main 方法(线程)中,创建线程对象,并启动线程
Thread1 th1 = new Thread1();
th1.start();
}
}
}
}
实现Runnable接口
class Runnable1 implements Runnable{
//重写Runnable接口的run()方法
public void run() {
for(int i = 0 ; i < 10 ;i++){
System.out.println("play music " + i);
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
for(int i = 0 ; i < 10 ; i++){
System.out.println(“play games " + i);
if(i==3){
//创建线程类:Thread t = new Thread(new A类);
Thread th1 = new Thread(new Runnable1());
th1.start();
}
}
}
}
继承Thread类
易受JAVA单继承特点的限制;
每个对象都是一个线程,均具有各自的成员变量。
实现Runnable接口
对象可自由地继承自另一个类;
对象不是线程,须将其作为参数传入Thread对象才能运行;
线程间可共享同一个接口实现类的对象,更适合多个线程共享数据的情况。
3.常用方法
Thread类
static Thread currentThread()
返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类。
String getName()
返回线程的名称。
void setName(String name)
设置该线程名称。
static void yield()
暂停当前的执行线程,把执行机会让给优先级相同或更高的线程。
final void join()
当线程A中调用线程B的join() 方法时,线程A将被阻塞,直到线程B执行完为止,线程A才结束阻塞。
static void sleep(long millitime)
让当前线程“睡眠”指定的时长。在该时间内线程处于阻塞状态。
final void setPriority(int priority))
设置当前线程的优先级。
final int getPriority()
获取当前线程的优先级。
final boolean isAlive()
判断当前线程是否存活。
4.线程的生命周期
线程一旦死亡,就不能再通过start()方法复生。
5.多线程的同步问题
JAVA同步机制
Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例有关的锁(该过程又称获取锁、锁定对象、在对象上锁定或在对象上同步)。
每个对象只有一个锁。当一个线程获得该锁,任何其他线程就不能再进入该对象上的synchronized方法或代码块,直到该锁被释放。
同步方法
synchronized void method( ) {
//同步的方法
}
同步代码块
只希望防止多个线程同时访问某个方法内部的部分代码,此时无需对整个方法都进行锁定
synchronized( target ){ target.call(msg); }
使用Lock锁机制(了解即可)
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
ReentrantLock类是Lock接口最常用的实现类,拥有与synchronized 相同的并发性和内存语义。
class TicketSellRunnable implements Runnable{
private int num = 10;
Lock l = new ReentrantLock();
public void run() {
for(int i = 0 ; i < 10 ;i ++){
l.lock();
try {
//同步代码块置于try{}中。
if(num > 0){
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+
" sells one ticket, "+(--num)+" left.");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
}
synchronized 与Lock 的对比
Lock是显式锁(需手动开启和关闭锁); synchronized是隐式锁,出了作用域自动释放;
Lock只有代码块锁,synchronized有代码块锁和方法锁;
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好且具有更好的扩展性(提供更多的子类);
第十四章:IO操作
Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行
1.认识IO操作
1)IO流的分类
按处理的数据单位分类
字节流
操作的数据单元是8位的字节。以InputStream、OutputStream作为抽象基类;
字符流
操作的数据单元是16位的字符。以Writer、Reader作为抽象基类。
字节流可以处理所有数据文件。但是,若处理的是纯文本数据,建议使用字符流。
根据流的作用分类
节点流
程序直接与数据源连接,和实际的输入/输出节点连接;
处理流
对节点流进行包装,扩展原来的功能,由处理流执行IO操作。
处理流可以隐藏底层设备上节点流的差异,无需关心数据源的来源,程序只需要通过处理流执行IO操作。实际应用中,一般推荐使用处理流来完成IO操作。
2)InputStream & Reader
//InputStream
int read(): 从输入流中读取数据的下一个字节。返回0到255范围内的int字节值。
int read(byte[] b): 从此输入流中将最多b.length个字节的数据读入一个byte数组中。
int read(byte[] b,int off,int len): 将输入流中最多len个数据字节读入byte数组。
public void close() throws IOException: 关闭此输入流并释放与该流关联的所有系统资源
//Reader
int read(): 读取单个字符。
int read(char[] cbuf): 将字符读入数组。
int read(char[] cbuf,int off,int len): 将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储
public void close() throws IOException: 关闭此输入流并释放与该流关联的所有系统资源
3)OutputStream & Writer
//OutputStream
void write(int b) : 将指定的字节写入此输出流。
void write(byte[] b): 将b.length个字节从指定的byte数组写入此输出流。
void write(byte[] b,int off, int len): 将指定byte数组中从偏移量off开始的len个字节写入此输出流。
public void flush() throws IOException: 刷新此输出流并强制写出所有缓冲的输出字节
public void close() throws IOException: 关闭此输出流并释放与该流关联的所有系统资源
Writer
void write(int c): 写入单个字符。
void write(char[] cbuf): 写入字符数组。
void write(char[] cbuf, int off, int len): 写入字符数组的某部分。
void write(String str): 写入字符串。
void write(String str, int off, int len): 写入字符串的某一部分。
void flush(): 刷新该流的缓冲,则立即将它们写入预期目标。
public void close() throws IOException: 关闭此输出流并释放与该流关联的所有系统资源。
2.File类
pava.io.File类:文件和文件目录路径的抽象表示形式,与平台无关。
File 能新建、删除、重命名文件和目录,但File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
File对象可以作为参数传递给流的构造函数。
1)构造函数
public File(String pathname)
以pathname为路径创建File对象,可以是绝对路径或相对路径,若pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
public File(String parent, String child)
以parent为父路径,child为子路径创建File对象。
public File(File parent, String child)
根据一个父File对象和子文件路径创建File对象。
2)路径分隔符
路径中的每级目录之间用一个路径分隔符隔开。
windows和DOS系统默认使用“\”来表示
UNIX和URL使用“/”来表示
File类提供了一个常量:public static final String separator。根据操作系统,动态的提供分隔符。
3)创建功能
public boolean createNewFile()
创建文件。若文件存在,则不创建,返回false。
public boolean mkdir()
创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
public boolean mkdirs()
创建文件目录。如果上层文件目录不存在,一并创建。
4)删除功能
public boolean delete()
删除文件或者文件夹。
1.删除时不经回收站直接删除;
2.要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录;
3.不能删除非空文件夹。
5)其他功能
public String getAbsolutePath()
获取绝对路径。
public String getPath()
获取定义时的路径。
public String getName()
获取名称。
public String getParent()
获取上层文件目录路径。若无,返回null
public long length()
获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified()
获取最后一次的修改时间,毫秒值
public String[] list()
获取指定目录下的所有文件或者文件目录的名称数组。
public File[] listFiles()
获取指定目录下的所有文件或者文件目录的File数组。
public boolean renameTo(Filedest)
把文件重命名为指定的文件路径。
public boolean isDirectory()
判断是否是文件目录
public boolean isFile()
判断是否是文件。
public boolean exists()
判断是否存在。
public boolean canRead()
判断是否可读。
public boolean canWrite()
判断是否可写。
public boolean isHidden()
判断是否隐藏
3.流的分类
1)节点流(文件流)
import java.io.*;
public class CopyFile {
public static void main(String[] args) {
int dt;
FileInputStream fis;
FileOutputStream fos;
try{
fis = new FileInputStream("c:\\file\\file1.doc");
}catch(FileNotFoundException e){
System.out.println("源文件未找到");
return;
}
try{
fos = new FileOutputStream("c:\\file\\file2.doc");
}catch(FileNotFoundException e){
System.out.println("目标文件打开失败");
return;
}
try{
while((dt=fis.read())!=-1) fos.write(dt);
}catch(IOException e){
System.out.println("文件读写出错");
}finally{
try{
fis.close();
fos.close();
}
catch(IOException e){
e.printStackTrace();
}finally{System.out.println("文件读写完毕");}
}
}
}
在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被覆盖。如果使用构造器FileOutputStream(file, true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。
在读取文件时,必须保证该文件已存在,否则报异常。
字节流操作字节,比如:.mp3,.avi,.rmvb, mp4,.jpg, .doc,.ppt
字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意 .doc, .excel, .ppt这些不是文本文件。
2)缓冲流
缓冲流要“套接”在相应的节点流之上
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区。
当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个字节(8Kb),存在缓冲区中,直到缓冲区内数据处理完了,才重新从文件中读取下一个8192个字节数组
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
使用方法flush()可强制将缓冲区的内容全部写入输出流。
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流。
如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区。
import java.io.*;
public class BufferReaderTest {
public static void main(String[] args) {
try{
FileReader in = new FileReader("c:\\file\\file1.txt");
BufferedReader br = new BufferedReader(in);
String line;
while((line = br.readLine()) != null){
System.out.print(line);
//读入数据时不包括行结束符,显示时补充换行显示
System.out.println();
}
br.close();
}catch(IOException e){
System.out.println(e.toString());
}
}
}
//字符流的例子
3)转换流
转换流提供了在字节流和字符流之间的转换。
InputStreamReader:将InputStream转换为Reader
OutputStreamWriter:将Writer转换为OutputStream
很多时候我们使用转换流来处理文件乱码问题,实现编码和解码的功能
InputStreamReader
实现将字节的输入流按指定字符集转换为字符的输入流。
需要和InputStream“套接”。
构造函数:
public InputStreamReader(InputStream in)
public InputSreamReader(InputStream in, String charsetName)
OutputStreamWriter
实现将字符的输出流按指定字符集转换为字节的输出流。
需要和OutputStream“套接”。
构造函数:
public OutputStreamWriter(OutputStream out)
public OutputSreamWriter(OutputStream out, String charsetName)
public static void testConvert(File f) throws IOException {
if(!f.exists()) {
f.createNewFile();
}
//以System.in作为读取的数据源,即从键盘读取
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//允许添加内容,不会清除原有数据源
BufferedWriter bw = new BufferedWriter(new FileWriter(f,true));
String s = null;
while(!(s = br.readLine()).equals("")) {
bw.write(s);
bw.newLine(); //空一行
}
bw.flush();
bw.close();
br.close();
}
//使用转换流对写入数据进行改进。
4)标准输入输出流
System.in的类型是InputStream
System.out的类型是PrintStream,其是OutputStream的子类
import java.util.Scanner;
class MyIO {
public static void main(String []args) {
String str = "";
Scanner rd = new Scanner(System.in);//JDK5提供Scanner
while(rd.hasNext()) {
str = rd.next(); //读入字符串数据
System.out.print(str); //显示刚输入字符串
}
}
}
★next()与nextLine()的区别
next()
一定要读取到有效字符后才可以结束输入
对输入有效字符之前遇到的空白,next()方法会自动将其去掉
只有输入有效字符后才将其后输入的空白作为分隔符或者结束符
next()不能得到带有空格的字符串
nextLine()
以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符
可以获得带空白的字符串
5)打印流
打印输出指定内容,实现将基本数据类型的数据格式转化为字符串输出,根据构造参数中的节点流来决定输出到何处。
PrintStream :打印输出字节数据。
PrintWriter : 打印输出文本数据。
提供了一系列重载的print()和println()方法,用于多种数据类型的输出。
System.out返回的是PrintStream的实例。
import java.io.*;
public class printTest {
public static void main(String[] args) {
try{
OutputStream os = new FileOutputStream("c:\\file\\data.txt");
PrintStream ps = new PrintStream(os);
for(int i=1; i<=9; i++){
for(int j=1; j<=i; j++){
ps.printf("%8s",i+"*"+j+"="+(i*j));}
ps.println();
}
ps.close();
os.close();
}catch(IOException e){System.out.println(e.toString());}
}
}
6)数据流
数据流有两个类: DataInputStream和DataOutputStream,分别用于读取和写出基本数据类型、String类的数据
DataInputStream中的方法
7)对象流
可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
(只有字节流没有字符流)
类必须实现Serializable接口
给类加个序列化编号,给类定义一个标记,新修改后的类还可以操作曾经序列化的对象
静态是不能被序列化的,序列化只能对堆中的成员进行序列化 ,不能对“方法区”中的进行序列化
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("data.txt"));
Person p=new Person("1");
oos.writeObject(p);
oos.flush();
oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileOutputStream("data.txt"));
Person p1=(Person) ois.readObject();
System.out.println(p1.toString());
ois.close();
import java.io.Serializable;
public class Student implements Serializable {
//给类加个序列化编号
private static final long serialVersionUID = 9078616504949971001L;
...
}
8)随机存取文件流
RandomAccessFile 声明在java.io包下,但直接继承于java.lang.Object类。它实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以读也可以写
RandomAccessFile 类支持“随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件。
RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置
long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到pos
//构造函数
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
//注:mode 参数指定RandomAccessFile的访问模式:
//r: 以只读方式打开
//rw:打开以便读取和写入