Java学习笔记
1、Java特征
- 继承
- 抽象 (也有三大特征的说法,没有抽象)
- 多态
- 封装
2、Java基本数据类型
1、byte,short,int,long,float,double,char,boolean共8种
2、byte有效位1字节,short有效位2字节,int有效位4字节,long有效位8字节,float有效
位4字节,double有效位8字节,char有效位2字节,boolean有效位1字节
Java标识符组成
-
类名,接口名,枚举名采用驼峰命名,每个单词首字母大写
(如StudentAge) -
变量名和方法名采用匈牙利命名,从第二个单词首字母大写(如studentAge)
-
常量名全部大写,单词组合使用下划线连接
-
包名和工程名全部小写
数据类型转换
一种是自动转换:从小的数据类型到大的数据类型,从子类到父类。
一种是强制转换:从大的数据类型到小的数据类型,从父类到子类。
注意:
byte+byte=int short+short =int byte+short=int char+char =int
关键字
instanceof
-
Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的
类的实例,返回 boolean 的数据类型。
严格来说,instanceof 是 Java 的一个二元操作符(双目运算符),类似于 ==,>,< 等操作符。用来测试一个对象是否是为一个类的实例,
用法为:boolean result = obj instanceof class -
其中 obj 为一个对象,Class 表示一个类或者一个接口。
当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
static
- 在Java类中声明变量、方法和内部类时,可使用关键字static做为修饰符。
- static标记的变量或方法由整个类(所有实例)共享,如访问控制权限允许,可不必创建该类对象而直接用类名加‘.’调用。
- static成员也称类成员或静态成员,如:类变量、类方法、静态方法等。如果想让一个类的所有实例共享据,请用类变量(static关键字)
注意:
static与abstract不能连用:
因为static修饰的方法是静态方法,其可以直接被类所调用。而abstract
修饰的方法为抽象方法,即无方法体的方法,不能够被直接调用需要在子类或实现
类中去编写完整的方法处理逻辑后才能使用。
静态方法中调用非静态成员不成立
这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使
用,而静态方法在使用前不用创建任何对象
super
在Java类中使用super来引用父类的成分
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造方法中调用父类的构造方法
super的追溯不仅限于直接父类
final
在Java中声明类、属性和方法时,可使用关键字final来修饰。
final标记的变量(成员变量或局部变量)即成为常量,只能赋值一次。
final标记的类不能被继承。提高安全性,提高程序的可读性。
final标记的方法不能被子类重写。增加安全性。
final标记的成员变量必须在声明的同时或在每个构造方法中显式赋值,然后才能使用。
Java多态
Java多态的实现具有三种充要条件
- 继承
- 重写父类方法(Ps:父类引用对象只能调用子类重写方法,无法调用重载方法与子类扩展的属性与方法)
- 父类引用指向子类对象
向上转型:通过子类对象(小范围)转化为父类对象(大范围),这种转换是自
动完成的,不用强制。
向下转型:通过父类对象(大范围)实例化子类对象(小范围),这种转换不是
自动完成的,需要强制指定。
##继承##
子类继承了父类,就继承了父类的方法和属性,子类不能继承父类中私有的(private)的成员变量方法
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
因而,子类通常比父类的功能更多。在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”
方法重载
方法重载就是在同一个类中允许同时存在一个以上的同名函数,只要它们的参数个数或类型不同,参数序不同即可。
在同一个类中方法只能被重载,不能被重写
方法重写
在子类中可以根据需要对从父类中继承来的方法进行改造—覆盖方法(方法的重置、重写),
在程序执行时,子类的方法将覆盖父类的方法。覆盖方法必须和被覆盖方法具有相同的方法名称、参数列表和返回值类型。
覆盖方法不能使用比被覆盖方法更严格的访问权限,子类覆盖方法的权限>=父类被覆盖方法的权限
只存在子类与父类中(包括直接父类和间接父类)
组合
组合创建整体类实例时,必须创建其所有局部类的实例,而对于继承,创建子类实例时,无须创建父类的实例
实例:有A,B两个类存在(不是接口),请你设计一个C类,使得C类可以调用A、B类中的属性与方法?
使用组合,在C类中设置A、B类的引用变量即可
因为A,B两个类已经存在,所以无法修改,所以不能使B类继承类,再使C类继承B类,使用多继承实现。
接口与抽象类
抽象类
使用修饰符abstract修饰的类,无法被实例化,可以被继承,子类必须实现父类中所有的抽象方法,否则子类必须修饰为抽象类
抽象类中可以有非抽象的构造方法,但不能有静态方法,即static和abstract不能连用。
抽象类不能被实例化,无法被private保护,因为抽象方法需要由子类实现,抽象类只能被单继承,而接口可以多继承并且可以由多个类实现。
抽象类特征
-
如果一个类中有抽象方法,那么这个类一定是抽象类,也就是说,使用关键字 abstract 修饰的 方法一定是抽象方法,具有抽象方法的类一定是抽象类。实现类方法中只有方法具体的实现
-
抽象类中不一定只有抽象方法,抽象类中也可以有具体的方法,你可以自己去选择是否实现这些方 法。
-
抽象类中的约束不像接口那么严格,你可以在抽象类中定义构造方法、抽象方法、普通属性、方 法、静态属性和静态方法
-
抽象类和接口一样不能被实例化,实例化只能实例化具体的类
接口
接口与抽象类的区别与联系
1.abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的),所有的成员方法都是abstract的。
3.abstract class和interface所反映出的设计理念不同。其实abstract class表示的是“is-a”关系,interface表示的是“has-a”关系。
4.实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
5.接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值,抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
6.接口中的方法默认都是 public,abstract 类型的。
7、抽象类和接口都不能直接实例化。如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
8、抽象类要被子类继承,接口要被类实现。
9、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。
10、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
内存
栈内存
用于存储局部变量,当数据使用完,所占空间会自动释放。
在方法中定义的一些基本类型的变量和对象的引用变量都在方法的栈内存中分配,当在一段代码块中定义一个变量时,Java就在栈内存中为这个变量分配内存空间,当超出变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立刻被另作他用。
堆内存
数组和对象,通过new 建立的实例都存放在堆内存中。
每一个实体都有内存地址。
实体中的变量都有默认初始化值。
实体不在被使用,会在不确定的时间内被垃圾回收器回收。
堆内存用来存放由 new 运算符创建的 数组或对象 ,在堆中分配的内存,由Java虚拟机的垃圾回收器来自动管理。在堆中创建了一个数组或对象后,同时还在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,引用变量实际上保存的是数组或对象在堆内存中的首地址(也称为对象的句柄),以后就可以在程序中使用栈的引用变量来访问堆中的数组或对象。
引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。引用变量就相当于是为数组或对象起的一个名称。而数组或对象本身在堆内存中分配,即使程序运行到使用 new 运算符创建数组或对象的语句所在的代码块之外,数组或对象本身所占据的内存也不会被释放,数组或对象在没有引用变量指向它时,会变为垃圾,不能再被使用,但仍然占据内存空间不放,在随后一个不确定的时间被垃圾回收器收走(释放掉),这也是Java比较占内存的原因。
Java有一个特殊的引用型常量null,如果将一个引用变量赋值为null,则表示该引用变量不指向(引用)任何对象。
堆内存和栈内存的区别如下
-
定义不同:堆内存是区别于栈区、全局数据区和代码区的另一个内存区域。堆允许程序在运行时动态地申请某个大小的内存空间。栈内存在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配
-
特点不同:堆内存实际上指的就是优先队列的一种数据结构,第一个元素有最高的优先权;栈内存实际上就是满足先进后出的性质的数学或数据结构。栈内存是存取速度比堆要快,仅次于寄存器,栈数据可以共享。
-
范围不同:堆内存中分配的内存需要程序员手动释放,如果不释放,而系统内存管理器又不自动回收这些堆内存的话动态分配堆内存,那就一直被占用。栈内存中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
实例:
一维数组是最简单的数组,其逻辑结构是线性表。要使用一维数组,需要先声明数组;分配空间;创建数组元素并赋值。
数据类型 [] 数组名; //声明一维数组
数组名 = new 数据类型[个数]; //分配内存给数组
在数组的声明格式里,“数据类型”是声明数组元素的数据类型,可以是Java语言中任意的数据类型,包括基本类型和引用类型。
与C/C++语言不同,Java语言在数组的定义中并不为数组元素分配内存,因此“[]”中不用给出数组中元素的个数(即数组的长度),但必须在为它分配内存空间后才可使用。
数组声明之后,接下来就要分配数组所需的内存,这时必须用运算符new,利用new运算符为数组元素分配内存空间的方式称为动态内存分配方式。
int [] x; //声明名称为x的int型数组
x = new int[10]; //x数组中包含有10个元素,并为这10元素分配内存空间
等号左边的 “int [] x ”相当于定义了一个特殊的变量x,x的数据类型是一个对 int 型数组对象的引用,x就是一个数组的引用变量,其引用的数组元素个数不定。等号右边的 “new int[10]” 就是在堆内存中创建一个具有10个int型变量的数组对象。其意义就是将右边的数组对象赋值给左边的数组引用变量。
这里我们先声明一个数组,例如:
int [] x; //定义了一个数组x
这条语句执行完成后的内存状态如图下所示。
这时只声明了数组,而没有对其分配内存空间。现在我们为数组元素分配内存空间。
x = new int[10]; //初始化数组
声明数组并分配相应的内存空间,引用变量指向数组对象
执行“x=new int[10];”后,在堆内存里创建了一个数组对象,为这个数组对象分配了10个整数单元,并将数组对象赋给了数组引用变量x。引用变量就相当于C语言中的指针变量,而数组对象就是指针变量指向的那个内存块。所以在Java内部还是有指针,只是把指针的概念对用户隐藏起来了,而用户所使用的是引用变量。
用户也可以改变x的值,让它指向另外一个数组对象,或者不指向任何数组对象。要想让x不指向任何数组对象,只需要将常量null赋给x即可。如执行“x = null;”语句后的内存状态如图下所示。
执行完“x = null;”语句后,原来通过new int[10]产生的数组对象不再被任何引用变量所引用,变成了所谓的“垃圾”,直到垃圾回收器来将它释放掉。
克隆
1.浅克隆:只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。
2.深克隆:是在引用类型的类中也实现了clone,是clone的嵌套,复制后的对象与原对象之间完全不会影响。
3.使用序列化也能完成深复制的功能:对象序列化后写入流中,此时也就不存在引用什么的概念了,再从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。
4.使用clone实现的深克隆其实是浅克隆中嵌套了浅克隆,与toString方法类似
浅克隆
我们再创建一个类Teacher 其中属性有(String name,int age,Student student)并实现Cloneable接口重写Clone()方法:
public class Teacher implements Cloneable{
private String name;
private int age;
private Student student;
public Teacher() {
super();
}
public Teacher(String name, int age, Student student) {
super();
this.name = name;
this.age = age;
this.student = student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
protected Object clone(){
Teacher teacher = null;
try {
teacher = (Teacher)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
}
再调用测试类主函数
public static void main(String[] args) {
Student s1 = new Student("Haku",16);
Teacher t1 = new Teacher("Sloth",28,s1);
Teacher t2 = (Teacher)t1.clone();
System.out.println(t1 == t2);
System.out.println(t1.getStudent() == t2.getStudent());
}
结果打印为:
false
true
Teacher的克隆成功了,但Teacher的属性student却没成功克隆,属于同一个对象,这就是浅克隆
那如何浅克隆修改为深克隆呢?
深克隆
我们修改一下Teacher的Clone()方法,Student实例类也要实现Cloneable接口,并重写Clone()方法
@Override
protected Object clone(){
Teacher teacher = null;
try {
teacher = (Teacher)super.clone();
teacher.student = (Student)this.student.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
protected Student clone() throws CloneNotSupportedException{
return (Student)super.clone();
}
排序
Comparable自然排序:
实现Comparable接口:public class Book implements Comparable
重写compareTo()方法实现自然排序(this在前:降序 this在后:升序)
@Override
public int compareTo(Book o) {
int num =(int) (this.price-o.price);
int num2 = num==0?this.name.compareTo(o.name):num;
return num2;
}
内部类实现
TreeSet<Book> ts = new TreeSet<Book>(new Comparator<Book>() {
@Override
public int compare(Book b1, Book b2) {
int num = (int) (b1.getPrice() - b2.getPrice());
int num2 = num == 0 ? b1.getName().compareTo(b2.getName()) : num;
return num2;
}
});
选择排序
工作原理:
第一次从待排序的数据元素中选出**最小(或最大)**的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
package exercise01;
public class selectionSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arrays = { 10, 52, 13, 54, 35, 67, 37, 88, 39, 180 };
selectionSort(arrays);
for (int array : arrays) {
System.out.println(array);
}
}
private static int[] selectionSort(int[] arrays) {
for (int i = 0; i < arrays.length - 1; i++) {
//设置最小的数
int min = i;
for (int j = i + 1; j < arrays.length - 1; j++) {
//找到这一轮中比最小的数min小的数,取得下标
if (arrays[j] < arrays[min]) {
min = j;
}
}
//后面的数小于min,则交换使后面的数成为最小的数
swap(arrays, i, min);
}
return arrays;
}
private static void swap(int[] arrays, int i, int min) {
int temp = arrays[i];
arrays[i] = arrays[min];
arrays[min] = temp;
}
}
冒泡排序
每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。
而 “每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。
package exercise01;
public class BubbleSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arrays = { 10, 52, 13, 54, 35, 67, 37, 88, 39, 180 };
bubbleSort(arrays);
for (int array : arrays) {
System.out.println(array);
}
}
public static <arrays> int[] bubbleSort(int[] arrays) {
for (int i = 0; i < arrays.length - 1; i++) {
for (int j = 0; j < arrays.length - i - 1; j++) {
int temp;
if (arrays[j] > arrays[j + 1]) {
temp = arrays[j];
arrays[j] = arrays[j + 1];
arrays[j + 1] = temp;
}
}
}
return arrays;
}
}
快速排序
算法思路
快速排序是通过多次比较和交换来实现排序,在一趟排序中把将要排序的数据分成两个独立的部分,对这两部分进行排序使得其中一部分所有数据比另一部分都要小,然后继续递归排序这两部分,最终实现所有数据有序。
大致步骤如下:
1、首先设置一个分界值也就是基准值又是也称为监视哨,通过该分界值将数据分割成两部分。
2、将大于或等于分界值的数据集中到右边,小于分界值的数据集中到左边。一趟排序过后,左边部分中各个数据元素都小于分界值,而右边部分中各数据元素都大于或等于分界值,且右边部分个数据元素皆大于左边所有数据元素。
3、然后,左边和右边的数据可以看成两组不同的部分,重复上述1和2步骤
当左右两部分都有序时,整个数据就完成了排序。
package exercise01;
public class FastSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arrays = { 10, 52, 13, 54, 35, 67, 37, 88, 39, 180 };
fastSort(arrays,0,arrays.length-1);
for (int array : arrays) {
System.out.println(array);
}
}
private static void fastSort(int[] arr,int left,int right) {
// TODO Auto-generated method stub
int key = arr[left];
int begin = left;
int end = right;
if (left >= right) {
return;
}
while (left < right) {
while (arr[right] >= key && left < right) {
--right;
}
//比key小的数放在左边
arr[left] = arr[right];
while (arr[left] <= key && left < right) {
++left;
}
//比key大的数放在右边
arr[right] = arr[left];
}
//将key赋值给新的arr[left]
arr[left] = key;
//设置新的keyi,此时left就是key的下标
int keyi = left;
//递归,重复上述操作
fastSort(arr,begin,keyi-1);
fastSort(arr,keyi+1,end);
}
}
插入排序
package exercise01;
Spublic class InsertionSort {
/*
步骤:
1.从第一个元素开始,该元素可以认为已经被排序
2.取下一个元素temp,从已排序的元素序列从后往前扫描
3.如果该元素大于temp,则将该元素移到下一位
4.重复步骤3,直到找到已排序元素中小于等于temp的元素
5.temp插入到该元素的后面,如果已排序所有元素都大于temp,则将temp插入到下标为0的位置
6.重复步骤2~5
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arrays = { 10, 52, 13, 54, 35, 67, 37, 88, 39, 180 };
insertionSort(arrays);
for (int array : arrays) {
System.out.println(array);
}
}
public static void insertionSort(int []arrays){
for(int i=0;i<arrays.length-1;i++){
//记录有序序列最后一个给元素的下标
int end = i;
//待插入的元素
int temp = arrays[end+1];
//找到比待插入的数小的那个数,并取得下标
while(end >= 0){
//比插入的数大就往后移,直到前面的数比插入的数小为止
if(temp<arrays[end]){
arrays[end+1]=arrays[end];
end--;//插入的数是跟前面的数比较
}
else{
//前面的数比插入的数小时跳出循环
break;
}
}
//将待插入的数:temp,放在比待插入数小的那个数后面
arrays[end+1] = temp;
}
}
}
字符串
String
String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上:
String s1 = "Runoob"; // String 直接创建
String s2 = "Runoob"; // String 直接创建
String s3 = s1; // 相同引用
String s4 = new String("Runoob"); // String 对象创建
String s5 = new String("Runoob"); // String 对象创建
**注意:**String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类。
StringBuffer
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
public class RunoobTest{
public static void main(String args[]){
StringBuilder sb = new StringBuilder(10);
sb.append("Runoob..");
System.out.println(sb);
sb.append("!");
System.out.println(sb);
sb.insert(8, "Java");
System.out.println(sb);
sb.delete(5,8);
System.out.println(sb);
}
}
StringBuilder
系统类
八个包装类都是final类 Math也是final类
Byte byte
Short short
Integer int
Long long
Float float
Double double
Character char
Boolean boolean
Date类
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class TestDate {
public static void main(String[] args) throws ParseException {
Date date = new Date();
//1970-01-01至今的毫秒数
System.out.println(System.currentTimeMillis());
//当前时间
System.out.println(date);
//从1900加
Date date2 = new Date(2100,2,28);
System.out.println(date2);
Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
//得到当前的年、月、日、小时、分钟、秒、毫秒
System.out.println(c.get(Calendar.YEAR));
System.out.println(c.get(Calendar.MONTH) + ", " + c.get(Calendar.DAY_OF_MONTH));
System.out.println(" " + c.get(Calendar.HOUR_OF_DAY) + ", " + c.get(Calendar.MINUTE) + ", " + c.get(Calendar.SECOND) + ", " + c.get(Calendar.MILLISECOND));
//格式化日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(new Date()));
//2022-07-11 10:30:08 -> Mon Jul 11 10:29:06 CST 2022
Date date3 = sdf.parse("2022-07-11 10:29:06");
System.out.println(date3);
}
}
异常
常见的异常类
异常处理 | 说明 |
---|---|
Exception | 异常层次结构的父类 |
ArithmeticException | 算术错误情形,如以零作除数明 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
NullPointerException | 尝试访问 null 对象成员 |
ClassNotFoundException | 不能加载所需的类 |
IllegalArgumentException | 方法接收到非法参数 |
ClassCastException | 对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常,如把"abc"转换成数字 |
Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws
JVM默认处理方案:
·把异常的原因、名称、出现的位置输出在控制台
·程序停止执行
日志
### 设置 ###
log4j.rootLogger = debug,stdout,D,E,W
### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=D:/BigData/Log/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D:/BigData/Log/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=D:/BigData/Log/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =D:/BigData/Log/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=D:/BigData/Log/error.log
###DailyRollingFileAppender(每天产生一个日志文件) RollingFileAppender 目的地为大小受限的文件的Appender ###
log4j.appender.W = org.apache.log4j.DailyRollingFileAppender
###输出文件地址###
log4j.appender.W.File = D:/BigData/Log/warn.log
### Appender - 日志目的地,把格式化好的日志信息输出到指定的地方去 ###
#log4j.appender.file.Append=true,日志追加到前面的日志下面,
# 不会覆盖先前的日志文件,而为false时,会覆盖先前的日志文件
log4j.appender.W.Append = true
#这个配置的级别是输出到文件中的级别,
# 而log4j.rootLogger=info,console,file中的info是输出到控制台的级别
log4j.appender.W.Threshold = WRAN
#org.apache.log4j.HTMLLayout(以HTML表格形式布局),
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
log4j.appender.W.layout = org.apache.log4j.PatternLayout
log4j.appender.W.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
测试类
package com.log;
import org.apache.log4j.Logger;
import java.util.Scanner;
public class Test {
//获取日志记录器,使用static修饰,防止logger变量被GC
private static final Logger logger = Logger.getLogger(Test.class);
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入除数:");
int a = sc.nextInt();
logger.debug("除数: " + a);
System.out.println("请输入被除数:");
int b = sc.nextInt();
logger.debug("被除数: " + b);
int c = a / b;
logger.debug("DEBUG结果: " + c);
System.out.println("结果:" + c);
} catch (Exception e) {
logger.debug("debug", e);
logger.debug("debug" + e.getMessage());
} finally {
System.out.println("========== ");
}
// // 记录debug级别的信息
// logger.debug("This is debug message.");
// // 记录info级别的信息
// logger.info("This is info message.");
// // 记录error级别的信息
// logger.error("This is error message.");
// // 记录warn级别的信息
// logger.warn("This is warn message.");
}
}