=============================数据类型=============================
心得:
数组变量是一个引用。
垃圾回收机制。
switch语句里的default分支只有前面不执行才执行
将一个 float 或 double 值造型成整数值后,总是将小数部分“砍掉”,不作任何进位处理。
当然,最后的结果是在 Math.random() 的输出中包括了 0.0。或者用数字语言表达,输出值范围是[0,1)。
将小范围变量转换为大范围的变量称为拓宽类型,拓宽类型不需要显式转换,缩窄类型必须显式完成。
=============================面向对象=============================
/*
类的修饰符只有两个,默认和public的
类public后,成员也要public,不同包的类才能访问
public protected default private
同一个类中 ok ok ok ok
同一个包中 ok ok ok
不同包子类 ok ok
不同包 ok
*/
在这个包内,任何人都可使用那些没有访问指示符
的方法。例如, Detergent 将不会遇到任何麻烦。然而,假设来自另外某个包的类准备继承Cleanser ,它就
只能访问那些 public 成员。所以在计划继承的时候,一个比较好的规则是将所有字段都设为private,并将
所有方法都设为 public
构造器是一个类创建对象的根本途径,如果一个类没有构造器,这个类通常无法创建实例。因此如果定义了一个类而没有写构造器,系统会默认添加一个空参数的构造器。
- 初始化块(构造代码块)
语法:
[修饰符]{
//可执行语句。
}
注意:修饰符只能出现static。
初始化块没有名字,只能被“隐式”调用。
调用时机:实例初始化块总是在构造器执行之前执行,不能接收参数。
使用区别:给所有对象进行统一初始化,构造函数给对应的对象初始化。
- jdk编译器会把[实例初始化块代码][声明field指定初始值代码]都提取到构造器的最前面。
顺序是按它们在源代码中的顺序。
静态初始化块是类相关的,用于对整个类进行初始化处理,通常用于对类field执行初始化处理。静态初始化块不能对实例field进行初始化处理。
执行顺序:类初始化阶段,先执行最顶层父类的静态初始化块,然后依次向下,直到执行当前类的静态初始化块。对象初始化阶段,先执行最顶层父类的初始化块、最顶层父类构造器,然后依次向下,直到执行当前类的初始化块、当前类的构造器。
Person p = new Person();这句话做了什么事情?
1.找到Person.class文件并加载到内存中。
2.执行static代码块,如果有的话,
3.在堆内存中开辟空间,分配内存地址。
4.
static 是一个特殊的关键字,它可用于修饰方法,field等成员。static修饰的成员表明它属于这个类本身,而不属于该类的单个实例,因此通常把static修饰的field和方法称为类field和类方法。不使用static修饰的普通方法、field则属于该类的单个实例,而不属于该类,这也称为实例field、实例方法。
this关键字的最大的作用是让类中一个方法,访问该类里的另一个方法或field。this出现在某个方法体中时,只有当这个方法被调用时,它所代表的对象才被确定下来,谁在调用这个方法,this就代表谁。
java允许对象的一个成员直接调用另一个成员,可以省略this。大部分时候,一个方法访问该类中定义的其他方法、field时加不加this的效果是完全一样的。
static修饰的方法中不能使用this引用,java规定:静态成员不能直接访问非静态成员。
方法重载
java允许同一个类里定义多个同名方法,只要形参列表不同就行。如果同一个类中包含了两个或两个以上方法的方法名相同,但形参列表不同,则被称为方法重载。
方法重载的要求就是两同一不同:同一个类中,方法名相同,形参列表不同。至于方法的其他部分,如方法返回值类型、修饰符等,与方法重载没有任何关系。
局部变量
局部变量根据定义形式的不同,又可以分为以下三种:
--形参:在定义方法签名时定义的变量,形参的作用域在整个方法内有效。
--方法局部变量:在方法体内定义的局部变量,它的作用域是从定义该变量的地方生效,到该方法结束时失效。
--代码块局部变量:在代码块中定义的局部变量,它的作用域从定义该变量的地方生效,到该代码块结束时失效。
注意:与成员变量不同的是,局部变量除了形参之外,都必须显式初始化。(也就是说,必须先给方法局部变量和代码块局部变量指定初始值,否则不可以访问它们。
局部变量给匿名内部类使用的时候,在java7或者之前必须加final,java8则自动加上。
同一个类里,成员变量的作用范围是整个类内有效,一个类里不能定义两个同名的成员变量,即使一个是类field,一个是实例field也不行。
同一个方法里方法局部变量与形参不能同名。
同一个方法中,不同代码块内的代码块局部变量可以同名;如果先定义代码块局部变量,后定义方法局部变量,前面的代码块局部变量与后面的方法局部变量也可以同名。
Java允许局部变量和成员变量同名,因为局部变量会覆盖成员变量,如果需要在这个方法里引用被覆盖的成员变量,则可以使用this(对于实例field)或者类名(对于类field)作为调用者来限定访问成员变量。
继承中方法重写
要遵循“两同两小一大”:两同即方法名、形参列表相同,两小指的是子类方法的返回值类型应比父类的更小或相同,
子类方法声明抛出的异常类应比父类的更小或相同,“一大”指的是子类方法的访问权限应比父类的更大或相同。
注意:覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
当子类覆盖父类方法后,子类的对象将无法访问父类中被覆盖的方法,但可以在子类的方法中调用父类中被覆盖的方法。
如果需要在子类方法中调用父类中被覆盖的方法,则可以使用super(被覆盖的是实例方法)或父类类名(被覆盖的是类方法)作为调用者来调用父类中被覆盖的方法。
如果父类方法具有private访问权限,则该方法对其子类是隐藏的,因此其子类无法访问该方法,也无法重写该方法。如果子类中定义了一个与父类private方法具有相同的方法名、相同的形参列表、相同的返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。
静态字段和方法能被继承,不能被覆盖。但是可以被隐藏。
静态方法的调用根本不取决于对象的实际类型,而只是声明的类型。
以下结论未证实:
子类继承父类的所有(包括私有属性),但是却不能调用父类的私有属性,父类私有的属性方法等子类都不能调用,所以子类里用的getName()在父类里是public的。这是java的一个特性,叫封装。
重载和重写:重载发生在同一个类的多个同名方法之间,而重写发生在子类和父类的同名方法之间。
调用父类构造器
子类不会获得父类的构造器,但子类构造器里可以调用父类构造器的初始化代码。在一个构造器里调用另一个重载的构造器使用this,调用父类构造器使用super,都必须出现在子类构造器的第一行。
不管我们是否使用super调用父类构造器,子类构造器总会调用父类构造器一次。当中有如下情况:
1、使用super显式调用父类构造器,系统将根据super里传入的参数调用父类相应的构造器。
2、使用this显式调用本类中重载的构造器,系统将根据this里传入的参数调用本类另一个构造器,执行本类另一个构造器时即会调用父类构造器。
3、子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。
多态
当把一个子类对象直接赋给父类引用变量时,这个父类引用变量的编译时类型是父类,而运行时类型是子类,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,这就出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。
注意:父类不能调用其子类特有的方法,否则编译无法通过。对象的field不具备多态性,通过引用变量来访问其包含的实例field时,系统总是访问它编译时类型所定义的field,而不是它运行时类型所定义的field。
编写java程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。如果需要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型。
强制类型转换要注意:引用类型之间的转换只能在具有继承关系的两个类型之间进行,可以先使用instanceof运算符判断,其前面是对象,后面是类,如果前面的对象是后面的类的实例或者其子类实例,则返回true。
包装类
可以通过对应包装类的构造器来实现,8个包装类中除了Character之外,还可以通过传入一个字符串参数来构建包装类对象。
如果希望获得包装类对象中包装的基本类型变量,则可以使用包装类提供的xxxValue()实例方法。
除此之外,包装类还可以实现基本类型变量和字符串之间的转换。把字符串转换成基本类型的值,可以采用包装类提供的parseInt(String s)静态方法(除了Character其他都包含);而String类重载了多个ValueOf方法,用于将基本类型变量转换成字符串。
包装类实例->基本数据类型:实例.xxxValue()
基本数据类型->包装类实例:对应包装类的构造器(xxx x) 或 对应包装类.valueOf(xxx x)
字符串->基本数据类型:包装类.parseXxx(String s) 或 包装类构造器(String s)
基本数据类型->字符串:String.valueOf(基本类型)
final修饰符
final修饰的成员变量必须由程序员显式地指定初始值。而能指定初始值的地方如下:类field:必须在静态代码块中或声明该变量时指定初始值。实例field:必须在非静态代码块、声明该变量或构造器时指定初始值。
系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。使用final修饰的形参不能被赋值。
final修饰的引用变量,地址不改变,即一直引用同一个对象,但这个对象完全可以发生改变。
final修饰的方法不能被重写。
final修饰的类不能被继承。
抽象类
只有函数定义,没有函数体的函数称为抽象函数。
如果一个类中含有抽象函数,这个类必须声明为抽象类,类中没有抽象函数,这个类也可以声明为抽象类。
抽象类可以有构造函数。
子类继承抽象类(有抽象函数)必须复写这个抽象函数。
抽象类不能被实例化,即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。
抽象类的构造器不能用于创建实例,主要是用于被子类调用。
abstract不能和final、static、private共用。
package
default:同一个包中可访问
如果子类和父类不在同一个包,则子类无法使用父类中default权限的成员变量和成员函数
private只能本类使用,public可以被所有类使用,跨包时父类protected修饰的成员,子类也可以使用
创建线程
第一种:
1.继承Thread类2.复写public void run();3.生成线程类对象。4.启动线程:ft.start()就进入了就绪状态。
第二种:
1.实现Runnable,复写public void run() 2.生成对象 3.生成一个Thread对象,并将Runnable接口实现类的对象作为参数传递给Thread对象 4.通知Thread对象执行start方法
接口
一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。
由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块。接口里可以包含field(只能是常量)、方法(只能是抽象方法)、内部类。
接口里所有的成员,包括常量、方法、内部类和枚举都是public访问权限。
在接口里定义field时,不管是否使用public static final修饰符,接口里的field总是使用这三个修饰符修饰。而且,接口里没有构造器和初始化块,因此接口里定义的field只能在定义时指定默认值。
接口里定义的方法,不管是否使用public abstract修饰符,接口里的方法总是使用public abstract来修饰。
注意:实现接口方法时,必须使用public访问控制修饰符,因为接口里的方法都是public的,而子类重写父类方法时访问权限只能更大或者相等,所以实现类的方法只能用public修饰。
接口跟抽象类用法上有如下差别:
1、接口里只能定义抽象方法,而抽象类可以有普通方法。
2、接口里不能定义静态方法,而抽象类可以有静态方法。
3、接口里只能定义静态常量field,而抽象类既可以定义普通field,也可以定义静态常量field。
4、接口里不包含构造器,而抽象类有构造器,它并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
5、接口里不能包含初始化块,但抽象类可以包含初始化块。
6、接口可以继承多个父接口,类只能继承一个父类(或实现多个接口)。
内部类
内部类的唯一好处就是可以方便地访问外部类中的私有属性。
静态内部类,静态不能访问非静态的外部类属性。
方法中定义的内部类不能直接访问方法中的参数,如想在内部类所访问,则参数前必须加final
非静态内部类
在非静态内部类里可以直接访问外部类的private成员,这是因为在非静态内部类的对象里,保存了一个它寄存的外部类对象的引用(当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,而非静态内部类实例必须寄存在外部类实例里)。
当在非静态内部类的方法内访问某个变量时,系统优先在该方法内查找是否存在该名字的局部变量,如果存在就使用该变量;如果不存在,则到该方法所在的内部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果不存在,则到该内部类所在的外部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果依然不存在,系统将出现编译错误:提示找不到该变量。
因此,如果外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可通过使用:外部类类名.this、this作为限定来区分。
非静态内部类的成员可以访问外部类的private成员,但反过来就不成立了。
根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。
java不允许在非静态内部类里定义静态成员,即非静态内部类里不能有:静态方法、静态field和静态初始化块。
静态内部类
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此用static修饰的内部类称为类内部类(静态内部类)。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。
java也运行在接口里定义内部类,默认使用public static修饰,也就是说,接口的内部类只能是静态内部类。
使用内部类
下面分三种情况讨论内部类的用法。
(1)在外部类内部使用内部类
要注意的是:不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员。
(2)在外部类以外使用非静态内部类
如果希望在外部类以外的地方访问内部类(包括静态和非静态两种),则内部类不能使用private访问控制权限,private修饰的内部类只能在外部类内部使用。
定义内部类(静态或者非静态)变量的语法如下:OuterClass.InnerClass varName,如果有包名,还应该增加包名前缀。
因为非静态内部类的对象必须寄存在外部类的对象里,因此创建非静态内部类之前,必须先创建其外部类对象。语法如下:外部类实例.new Inner();从这可以总结出一条规则:非静态内部类的构造器必须使用外部类对象来调用。
有一个比较怪的语法,在外部类以外的地方创建非静态内部类的子类,代码如下:
public class SubClass extends Out.In{
//显式定义子类的构造器
public SubClass(Out out){
//通过传入的Out对象显式调用In的构造器
out.super();
}
}
(3)在外部类以外使用静态内部类
因为静态内部类是外部类相关的,因此创建静态内部类对象时无须创建外部类对象。语法:new 外部类类名.内部类();
可以看出:静态内部类只需使用外部类类名即可调用内部类构造器,而非静态内部类必须使用外部类对象来调用构造器。
创建静态内部类的子类也比较简单:public class StaticSubClass extends StaticOut.StaticIn{}
局部内部类
如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。
匿名内部类
匿名内部类不能定义构造器,可以定义实例初始化块,通过实例初始化块来完成构造器需要完成的事情。
new 接口名后的括号不能传入参数;而通过new 类名创建匿名内部类拥有和此类相似的构造器,可传参数。
注意:匿名内部类需要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量。
枚举
枚举类与普通类有如下区别:
1、使用enum定义的枚举类默认继承了java.lang.Enum类,而不是继承Object类,其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。
2、使用Enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
3、枚举类的构造器只能使用private访问控制符,如果省略,默认也是使用private
4、枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远不能产生实例。列出实例时,系统自动添加public static final 修饰,无须程序员显式添加。
=============================数组与集合=============================
- 数组
数组也是一种类型,如int[] String[] ......
初始化:
1.静态初始化 int[] arr = new int[]{};
或者简化的形式 int[] arr = {};
2.动态初始化
int[] arr = new int[5];
系统自动为数组元素分配初始值
整数类型(byte short int long) 赋值0
浮点类型(float double) 赋值0.0
字符类型(char) 赋值'\u0000'
布尔类型 false
引用类型 null
集合,也叫容器,它是用来装“对象”的。
有哪些通用方法?
- 存东西
- “读取”出来,但它仍然在容器中。
- 把东西从容器中删除。
- 遍历
- 把容器倒空。
- A容器的东西,全部倒入B容器中。集合 + 集合
- A容器的东西,减去B容器包含的东西。集合 -集合
- A容器与B容器都共同有的东西。 集合 ∩ 集合
HashSet存储机制:
底层用一个数组存元素 - 数组长度永远是2的N次方。
构造器HashSet(int initialCapacity,double loadFactor),如果传的不是2的N次方,会自动扩展。
默认值initialCapacity 16,loadFactor 0.75
取元素机制:
1.调用hashCode()方法,得到int值。
2.根据int值,计算底层数组的存储位置。
3.刚好找到,直接取出。
4.如果该位置有链表,要“挨个”搜索链表里的元素。
HashSet怎么才认两个对象相等?
A.hashCode返回值相等 B.equals方法比较返回true
HashSet 子类 LinkedHashSet
它与HashSet存储机制相似。
LinkedHashSet额外维护一个链表,遍历时可记住元素的添加顺序。
TreeSet
特征:保证set里的元素是“大小排序”的。
存入机制:底层用一棵“红黑树”存放所有数据。
存入、检索的性能也很好。
TreeSet不会像HashSet那样有“空桶”。
TreeSet可以保证元素按“大小”排序。
使用TreeSet有一个要求:
要求集合元素必须是可以比较大小。
java的比较大小有两种方式:
A.自然排序 集合元素实现Comparable接口。
B.定制排序
集合怎么才认为两个对象相等?
答:
Set判断是根据equals()方法。
HashSet如果比较hashCode方法相等,再比较equals方法true。
TreeSet只要两个对象通过compareTo比较返回0,TreeSet认为它们是相等的。
List 判断两个对象相等的标准是:equals()方法返回true即可。
在hashSet没有大量出现链表的情况下,HashSet比TreeSet性能好。
如果HashSet经常发生"重hash",性能略差。
ArrayList
包含了大量根据索引来存、取元素的方法。
多一个遍历集合元素的方法。
与Vector区别:Vector是一个古老的集合。ArrayList线程不安全,Vector是线程安全。
ArrayList性能更好。即使在多线程环境下,用Collections类转为线程安全的。
LinkedList
ArrayList:由于根据底层数组的索引存、取元素,性能非常快。当插入、删除元素,后面的元素要整体搬家。
LinkedList:由于底层采用了链表来存储元素,因此根据索引存、取元素,所以性能较慢,当插入、删除元素,无须整体搬家,性能快。
Map
通用方法
1.添加元素
put方法,会返回原有的值
2.删除
3.获取
4.判断
containsKey()
Map不允许key重复
HashMap怎样才算两个key重复?
1.哈希值相等。2.equals比较返回true
TreeMap怎样才算两个key重复?
通过compareTo方法比较大小返回0,说明两个元素相等。
java集合有个缺点,当把一个对象丢进集合里后,集合就会忘记这个对象的数据类型,当再次取出该对象时,
该对象的编译类型就变成了Object。
hashSet存储对象的依据是哈希值,如果哈希值一样,再判断equals方法,比较是不是同一个对象;如果哈希值不一样,不用比较equals方法.
而arraylist依赖的是equals方法.
=============================常用类=============================
String类
str1.indexOf("c",3)//查找返回位置,从第4个开始查找
String两种实例化方式的区别
new会在堆内存重新开辟空间,而直接赋值如果内容相同,会指向同一个位置,(java提供一个字符串池来保存全部内容)
File类
File的构造器可以将已经有的和未出现的文件或者文件夹封装成对象。
获取相对路径的父路径可能出错,这时file.getParent()会返回null
创建临时文件:File tmpFile = File.createTempFile("aaa",".txt",file);
判断文件是否存在:file.exists()
创建文件:file.createNewFile()
创建单级目录:file.mkdir()//file必须是一个路径
列出当前路径下的所有文件和路径:
String[] fileList = file.list();
for(String fileName:fileList){
System.out.println(fileName);
}
列出所有磁盘根路径:
File[] roots = File.listRoots();
for(File root:roots){
System.out.println(root);
}
注意:f.getPath()如果你封装的是绝对路径,那么它返回的也是绝对路径,跟f.getAbsolutePath()一样;如果你封装的是相对路径,那么它就返回相对路径。
f1.renameTo(f2)次方法作用是将f1的文件名改成f2的文件名,f1如存在会返回true。
调用list方法的对象必须是封装了一个目录,该目录必须存在,如果不是目录或者不存在,将返回null;如果存在,目录没有内容,不会null,而是返回一个长度为0的数组。
File[] files = listFiles()功能比较强。
properties类
它是hashtable集合的子类,也就是说它具备map集合特点,而且它里面存储的都是字符串,是集合中和IO技术相结合的集合容器。常用于键值对形式的配置文件的操作。加载数据时需要数据有固定格式:键=值
常用方法:Set<String> prop.stringPropertyNames() 返回键的集合,类似还有Enumeration<?> propertyNames()
示例代码:
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
prop.load(fis);//封装了IO读取文件
prop.setProperty("wangwu","30");//修改数据
FileOutputStream fos = new FileOutputStream("info.txt");
prop.store(fos,"注释");//把修改写出到外存
//遍历
Set<String> names = prop.stringPropertyNames();
for(String s : names){
System.out.println(s+"="+prop.getProperty(s));
}
记录使用次数练习的代码:
Properties prop = new Properties();
//封装完的好处是可以做判断,避免异常
File file = new File("count.ini");
if(!file.exists()){
file.createNewFile();
}
FileInputStream fis = new FileInputStream(file);
prop.load(fis);
int count = 0;
String value = prop.getProperty("time");
//把值先拿出来,有null的可能,作判断处理
if(value!=null){
count = Integer.parseInt(value);
if(count>=5){
System.out.println("使用次数到!");
return;
}
}
/*自己的方式,容易理解,不够简洁
if(value==null){
prop.setProperty("time","0");
}else{
count = Integer.parseInt(value);
}
*/
count++;
prop.setProperty("time",count+"");
FileOutputStream fos = new FileOutputStream(file);
prop.store(fos,"");
fos.close();
fis.close();
System类:描述系统一些信息。有一个System.getProperties()方法,获取系统属性信息,返回的是Properties类型的Map集合。Properties是Hashtable的子类,也就是说是Map集合的子类对象,那么可以通过map的方法取出该集合的元素。下面是实例代码:
Properties prop = System.getProperties();
for(object obj:prop.keySet()){
String value = prop.get(obj);
System.out.println(obj+"::"+value);
}
如何在系统中自定义一些特有的信息呢?System.setProperty("mykey","myvalue");
获取指定属性信息:String value = System.getProperty("os.name");(跨平台时会用)
在虚拟机启动时动态加载一些属性信息:启动时 -D<key>=<value> 类名
Runtime类:
获取单例:Runtime r=Runtime.getRuntime();
执行程序:r.exec(String s);
Date类:
打印的时间看不懂,希望有些格式:
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日E hh:mm:ss");
String time=sdf.format(d);
Calendar类:
Calendar c=Calendar.getInstance();
获取年:c.get(Calendar.YEAR);
查表法:String[] weeks = {"星期日","星期一","星期二","星期三"};
int index = c.get(Calendar.DAY_OF_WEEK);
String week = weeks[index];
正则表达式
要记忆的知识点有:1、通配符2、数量词3、范围(其实也是数量)4、枚举(逻列)5、开始与结束
1、通配符:
\w:[a-zA-Z0-9]
\W[^a-zA-Z0-9]
\d:[0-9]
\D:[^0-9]
\s:空白符 空格
\S:非空白符
.:任意
2、数量词:
?:0-1次
+: 1-N次
*:0-N次
3、范围:
\d{1,10} :出现1到10次
\d{3}:必须出现3次
\d{3,}:至少出现3次
4、逻列:[3|5|8]
5、开始与结束:^:开始 $:结束
=============================泛型=============================
泛型
定义A继承Apple类,Apple不能跟类型参数。如:class A extends Apple<T>{}这样是错误的。
泛型方法public static <W> void method(W w):可操作任何类型的数据,位置:修饰符后,返回值类型前
静态方法不可以访问类上定义的泛型:如有class Demo<T>时,public static void method(T t) 是错误的
可以定义带泛型形参的实现类。
泛型的限定:?extends E:可接收E或E的子类(用在方法形参,扩展)
?super E:可接收E或E的父类(用在比较器,使程序扩展)
什么时候定义泛型类?当类中要操作的引用数据类型不确定的时候,可以用泛型类来完成扩展。
为了让方法可以操作不同的类型,类型还不确定,可以把泛型定义在方法上。public <T> void show(T t){};
静态方法不可以访问类上定义的泛型(方法的参数类型不能是类上的泛型)。如果静态方法操作的数据类型不确定,可以将泛型定义在静态方法上。public static <T> void show(T t){};泛型符号要放在修饰符后面,返回值前面。
接口也可定义泛型。定义实现类实现带泛型的接口,这时实现类和接口的泛型都可以未指定。创建实例的时候一定要指定。
第一步是获得你想操作的类的 java.lang.Class 对象
第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。
第三步了——使用 reflection API 来操作这些信息。
//m是一个Method对象,因为一个方法的参数可能有多个,所以参数类型就可能有多个,所以要返回数组
Class pvec[] = m.getParameterTypes();
调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你只获得定义在类中的public方法(public权限以外的无法获取),还能获得继承来的public方法的信息。
Field的方法:
--修改字段的值
public void set(Object obj, Object value)
参数:
obj - 应该修改其字段的对象
value - 正被修改的 obj 的字段的新值
--获取字段的值
public Object get(Object obj)
参数:
obj - 从中提取所表示字段的值的对象
返回:
对象 obj 中的所表示字段的值;在返回之前,基值包装在一个适当的对象中
public void setAccessible(boolean flag)
public boolean isAccessible()
经典代码:
if(!f.isAccessible()){
setAccessible(true);
}
从setAccessible方法可以发现,反射是破坏封装性的。
-----------------------------------------------------------------
Method
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息
Class的方法:
public Method[] getDeclaredMethods()
返回:
表示此类所有声明方法的 Method 对象的数组
public Method getDeclaredMethod(String name,
Class<?>... parameterTypes)
参数:
name - 方法名
parameterTypes - 参数数组
返回:
该类与指定名和参数相匹配的方法的 Method 对象
Method的方法:
执行Method对象所代表的方法
public Object invoke(Object obj,
Object... args)
参数:
obj - 从中调用底层方法的对象
args - 用于方法调用的参数
返回:
使用参数 args 在 obj 上指派该对象所表示方法的结果
----------------------------------------------------------------------
标注(Annotation)
JDK1.5最引人关注的新功能,对现代编程产生深远影响。其实注释是写给编译器看的。
自定义标注:在interface关键字前面使用@符号声明Annotation类型
属性声明方式:String name()或String[] likes();
属性默认值声明方式:String name() default "xxx";
特殊属性value:如果注解中有一个名称value的属性,那么使用时可省略value=
元Annotation
指修饰注解的Annotation,不能用在其他地方。
@Retention指示注释类型的注释要保留多久。如果注释类型声明中不存在Retention注释,则保留策略默认为RetentionPolicy.CLASS,运行时不保留注释。
@Retention(RetentionPolicy.RUNTIME):编译器将把注释记录在类文件中,在运行时VM将保留注释,因此可以用反射读取。
@target(ElementType.TYPE):指示注释类型所适用的程序元素的种类。(表明这个注解能用在哪)
拿到很多的方法后,想从这些方法里找是否有某注解,foreach遍历,判断如果get到的值不为空,那么调用方法invoke
代理
方法里有核心代码和非核心代码。非核心代码就可以通过代理来做。
定义一个代理类,定义一个方法,传一个对象,就把这个对象绑定起来,然后对它进行代理。返回被绑定的代理对象(接口)。
怎么绑定呢?jdk定义了一个专门的类来处理。
当拿到绑定的代理对象(接口),调用的时候,就会进入到代理者的处理程序invoke方法,这里就将决定做什么和能不能调用它原来的方法。这个invoke方法的第二个参数,是代理对象所调用方法的Method对象;第三个参数Object[] args就是代理对象调用方法时的实参,参数只有一个就是args[0]。返回值是Object,是方法调用返回的值:method.invoke(target,args)
public class DynamicProxy {
public Object bind(final Object delegate) {
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
delegate.getClass().getInterfaces(), new InvocationHandler() {
@Override
// 代理要做什么,就在这里写
/**第一个参数就是外面代理对象,一般不用
* 第二个参数就是外面代理对象调用的方法时,会把方法对象传进来
* 第三个参数就是外面代理对象执行方法时传进来的参数
* 返回值一般是 method.invoke(delegate, args)*/
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
return method.invoke(delegate, args);
}
});
}
}
自己实现beanUtil方法
Class<?> c = bean.getClass();
Field field = c.getDeclaredField(name);
//获取这个属性方法名的字符串
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method m = c.getDeclaredMethod(methodName, field.getType());
m.invoke(bean, value);
- 读取资源的方法
Class c = Class.forName("org.fkjava.test");
c.getResourceAsStream("/test.txt");// 读取资源文件的路径写法,/代表src根路径
=============================IO=============================
=============================注解=============================
=============================反射=============================
=============================多线程=============================
多线程出现安全问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,
另一个线程参与进来执行,导致共享数据错误。
进入同步代码块时把标志位关上,线程执行完才把标志位打开(让其他线程可进入)
new Thread(mt,"线程A").start();-- 手动设置线程名
Thread.currentThread().getName();-- 获取当前线程的名字
-- java程序启动,至少启动了两个线程,主线程和垃圾回收线程
-- isAlive()可以判断线程是否执行完(是否在启动状态)
-- 线程强制运行join()
-- 线程休眠 Thread.sleep()
-- 线程中断interrupt()
它会把线程的状态转变成中断的,对于执行wait、sleep、join等方法的线程会让它抛出InterruptedException.
interrupt方法用处:
1、【可以使线程继续执行】,就是在catch语句中写醒来后的逻辑,或由catch语句转回正常的逻辑。
2、【可以直接停止线程的运行】,在catch中什么也不处理,或return,那么就完成了当前线程的使命。
while(!isInterrupted()){
正常逻辑
}
如果线程正在执行wait、sleep、join方法,调用interrupt()应该如下:
public void run(){
try{
while(!isInterrupted()){
正常逻辑
}
}catch(Exception e){
return;
}finally{
清理工作
}
}
-- 设为后台线程:setDaemon(true)-- 此线程在后台运行
当所有的前台线程都结束后,后台线程会自动结束。
-- 线程的优先级:数字越大,级别越高
t.setPriority(Thread.MAX_PRIORITY);
t.setPriority(Thread.MIN_PRIORITY);
t.setPriority(Thread.NORM_PRIORITY);
-- join方法(临时加入线程)
当A线程执行到B线程的join方法时,A就会等待,等B线程都执行完,A才会执行。
-- 线程的礼让:Thread.yield()
-- 线程的同步(同步方式有两种,同步代码块和同步函数。)
方式一: 同步代码块
synchronized (this) {
}
方式二: 同步方法
有同步代码块时线程的运作过程:
线程一获取到CPU执行权,它就去判断obj的标志位为1,开着,就进来了。这个线程还做了一件事,就是把标志位变为0关上了,
接着继续执行。当执行完,出同步代码块的时候,它又做了一件事,就是把标志位又变为1
同步可以解决线程安全问题,但使用同步的前提:
1.必须要有两个以上线程。
2.必须是多个线程使用同一个锁。
等待的线程都存在线程池。notify()的时候唤醒的是线程池中的线程,一般是第一个被等待的.
只有同一个锁上的被等待线程,可以被同一个锁的notify唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
JDK1.5 有多线程的新解决方案:
Lock lock = new ReentrantLock();
Condition condition_pro = lock.newCondition();
Condition condition_con = lock.newCondition();
lock.lock();
lock.unlock();//必须执行,要放在finally块中。
//可以不全部唤醒,只唤醒对方,对应的signal()唤醒对应的await()
condition_pro.await();
condition_pro.signal();
冻结状态强制被清除了,那么就发生了中断异常。在异常里面把循环结束就可以结束线程。
当有代码需要同时运行时,就可以封装一下,采用多线程(匿名内部类方式使用更方便)
=============================网络编程=============================
java8新特性
函数式接口,可使用注解强制限定接口只能有一个抽象方法。
1.接口里面可以有普通非静态的方法,只能用default修饰。
default void test(){
}
多继承时:
interace C extends A,B{
default void test(){
B.super.test();
}
}
class D implements A,B{
public void test(){//注意public
A.super.test();
}
}
注意点:
1.继承了多个父接口,有重复 的默认方法被继承,