java主要特性:
- java语言是简单的
java语言不使用指针,而是引用。并提供了自动的废料收集,使得程序员不必为内存管理而担忧。 - java语言是面相对象的
java语言提供类、继承、接口等面相对象的特性 - java语言是分布式的
- java语言是健壮的
java的强类型机制、异常处理、垃圾的自动收集等是java健壮性的重要保证 - java语言是安全的
- java语言是体系结构中立的
- java语言是可移植的
- java语言是解释性的
- java语言是高性能的
- java语言是多线程的
在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。通常有两种方法来创建线程:其一,使用型构为Thread(Runnable)的构造子将一个实现了Runnable接口的对象包装成一个线程,其二,从Thread类派生出子类并重写run方法,使用该子类创建的对象即为线程。
Java语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为synchronized)。 - java语言是动态的
java基础语法
- 对象:
对象是类的一个实例,有状态和行为。 - 类
类是一个模板,他描述一类对象的状态和行为。 - 方法
方法就是行为,一个类可以有很多方法。 - 实例变量
每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
java修饰符
主要有两类修饰符:
访问控制修饰符:default, public , protected, private
非访问控制修饰符:final, abstract, strictfp
- default (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。 - public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
访问控制和继承:
- 父类中声明为 public 的方法在子类中也必须为 public。
- 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
- 父类中声明为 private 的方法,不能够被继承。
非访问控制符:
- static 修饰符,用来修饰类方法和类变量。
- final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
- abstract 修饰符,用来创建抽象类和抽象方法。抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。 - synchronized 修饰符。synchronized 关键字声明的方法同一时间只能被一个线程访问。
- volatile 修饰符,volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
- transient 修饰符,序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
java变量
主要有三种类型的变量
- 局部变量
在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
访问修饰符不能用于局部变量;
局部变量是在栈上分配的。
局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。 - 类变量(静态变量)
类变量也声明在类中,方法体之外,但必须声明为static类型。
静态变量储存在静态存储区。经常被声明为常量,很少单独使用static声明变量。静态变量可以通过:ClassName.VariableName的方式访问。 - 成员变量(非静态变量)
成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
成员变量与类变量的区别:
- 两个变量的生命周期不同
成员变量随着对象的创建而存在,随着对象的回收而释放。
静态变量随着类的加载而存在,随着类的消失而消失。 - 调用方式不同
成员变量只能被对象调用。
静态变量可以被对象调用,还可以被类名调用。 - 别名不同
成员变量也称为实例变量。
静态变量也称为类变量。 - 数据存储位置不同
成员变量存储在堆内存的对象中,所以也叫对象的特有数据。
静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。
特点:
- 想要实现对象中的共性数据的对象共享。可以将这个数据进行静态修饰。
- 被静态修饰的成员,可以直接被类名所调用。也就是说,静态的成员多了一种调用方式。类名.静态方式。
- 静态随着类的加载而加载。而且优先于对象存在。
弊端:
- 有些数据是对象特有的数据,是不可以被静态修饰的。因为那样的话,特有数据会变成对象的共享数据。这样对事物的描述就出了问题。所以,在定义静态时,必须要明确,这个数据是否是被对象所共享的。
- 静态方法只能访问静态成员,不可以访问非静态成员。
因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。 - 静态方法中不能使用this,super关键字。
因为this代表对象,而静态在时,有可能没有对象,所以this无法使用。
成员分两种:
- 成员变量。(数据共享时静态化)
该成员变量的数据是否是所有对象都一样:
如果是,那么该变量需要被静态修饰,因为是共享的数据。
如果不是,那么就说这是对象的特有数据,要存储到对象中。 - 成员函数。(方法中没有调用特有数据时就定义成静态)
如果判断成员函数是否需要被静态修饰呢?
只要参考,该函数内是否访问了对象中的特有数据:
如果有访问特有数据,那方法不能被静态修饰。
如果没有访问过特有数据,那么这个方法需要被静态修饰。
成员变量与局部变量的区别:
- 声明位置不同
成员变量也就是属性,在类中声明的。
局部变量,在方法中声明或代码块中声明。 - 初始值不同
成员变量如果没有赋值则是有默认值的,数据类型不同则默认值不同。
局部变量是没有默认值,也就是说必须先声明,再赋值,最后才使用。 - 在一个类中,局部变量可以与成员变量同名,但是局部变量优先,如果非要访问成员变量的属性,则必须使用 this.color
this 代表当前这个对象,也就是当前谁调用这个方法则这个对象就是谁。
对象与引用区别
对象是具体的一个实例,如:new Student(); new 表示创建一个对象,并在堆内存中开辟一块空间。
引用名称是存放的对象的地址。
构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
源文件声明规则
- 一个源文件中只能有一个public类
- 一个源文件可以有多个非public类
- 源文件的名称应该和public类的类名保持一致。
- 如果一个类定义在某个包中,那么package语句应该在源文件的首行。
- 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
- import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
java基本数据类型
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
java的两大数据类型:
- 内置数据类型
- 引用数据类型
内置数据类型:
类型 | 默认值 | 包装类 | 备注 |
---|---|---|---|
byte | 0 | java.lang.Byte | Byte.SIZE Byte.MIN_VALUE Byte.MAX_VALUE |
short | 0 | java.lang.Short | Short.SIZE Short.MIN_VALUE Short.MAX_VALUE |
int | 0 | java.lang.Integer | Integer.SIZE Integer.MIN_VALUE Integer.MAX_VALUE |
long | 0L | java.lang.Long | Long.SIZE Long.MIN_VALUE Long.MAX_VALUE |
float | 0.0f | java.lang.Float | Float.SIZE Float.MIN_VALUE Float.MAX_VALUE |
double | 0.0d | java.lang.Double | Double.SIZE Double.MIN_VALUE Double.MAX_VALUE |
boolean | false | ||
char | java.lang.Character | Character.SIZE Character.MIN_VALUE Character.MAX_VALUE |
自动类型转换:
必须满足转换前的数据类型的位数要低于转换后的数据类型
byte,short,char—> int —> long—> float —> double
char c1='a';//定义一个char类型
int i1 = c1;//char自动类型转换为int
强制类型转换:
在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
转换过程中可能导致溢出或损失精度。
格式:(type)value type是要强制类型转换后的数据类型
int i1 = 123;
byte b = (byte)i1;//强制类型转换为byte
引用类型:
所有引用类型的默认值都是null。
类型转换的方法:
- 一般情况下,我们首先声明一个变量,然后生成一个对应的包装类,就可以利用包装类的各种方法进行类型转换了。
当希望把float型转换为double型时:
float f1=100.00f;
Float F1=new Float(f1);//Boolean(boolean value)、Character(char value)、Integer(int value)、Long(long value)、Float(float value)、Double(double value)
double d1=F1.doubleValue();//F1.doubleValue()为Float类的返回double值型的方法
- 其它类型向字符串的转换
- 调用类的串转换方法:X.toString();
- 自动转换:X+"";
- 使用String的方法:String.valueOf(X);
- 字符串作为值,向其它类型的转换
- 先转换成相应的封装器实例,再调用对应的方法转换成其它类型
例如,字符中"32.1"转换double型的值的格式为:new Float("32.1").doubleValue()。也可以用:Double.valueOf("32.1").doubleValue() - 静态parseXXX方法
String s = "1";
byte b = Byte.parseByte( s );
short t = Short.parseShort( s );
int i = Integer.parseInt( s );
long l = Long.parseLong( s );
Float f = Float.parseFloat( s );
Double d = Double.parseDouble( s );
- Date类与其它数据类型的相互转换
import java.text.SimpleDateFormat;
java.util.Date date = new java.util.Date();
//如果希望得到YYYYMMDD的格式
SimpleDateFormat sy1=new SimpleDateFormat("yyyyMMdd");
String dateFormat=sy1.format(date);
//如果希望分开得到年,月,日
SimpleDateFormat sy=new SimpleDateFormat("yyyy");
SimpleDateFormat sm=new SimpleDateFormat("MM");
SimpleDateFormat sd=new SimpleDateFormat("dd");
String syear=sy.format(date);
String smon=sm.format(date);
String sday=sd.format(date);
java运算符
- 算术运算符
- 关系运算符
- 位运算符
- 逻辑运算符
- 赋值运算符
- 其他运算符
算术运算符 | 关系运算符 | 位运算符 | 逻辑运算符 | 赋值运算符 | 其他运算符 |
---|---|---|---|---|---|
+、-、*、/、%、++、-- | ==、!=、>、<、>=、<= | &、|、^、~、<<、>>、>>> | &&、||、! | =、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^= | ?:、instanceof |
前缀自增自减法(++a,--a): 先进行自增或者自减运算,再进行表达式运算。
后缀自增自减法(a++,a--): 先进行表达式运算,再进行自增或者自减运算。
int a , b;
a = 10;
// 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真
java循环
- while循环
- do...while循环
- for循环
while( 布尔表达式 ) {
//循环内容
}
do {
//代码语句
}while(布尔表达式);
for(初始化; 布尔表达式; 更新) {
//代码语句
}
for(声明语句 : 表达式)
{
//代码句子
}
java分支结构
- if语句
- switch语句
if(布尔表达式 1){
//如果布尔表达式 1的值为true执行代码
}else if(布尔表达式 2){
//如果布尔表达式 2的值为true执行代码
}else if(布尔表达式 3){
//如果布尔表达式 3的值为true执行代码
}else {
//如果以上布尔表达式都不为true执行代码
}
switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}
java Number类、Math类、Character类、String类、StringBuffer类、StringBuilder 类
所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。Number 类属于 java.lang 包。
java中的自动装箱与拆箱:
简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
xxxValue() 方法用于将 Number 对象转换为 xxx基本 数据类型的值并返回。(byteValue() ,doubleValue(),floatValue(),intValue() ,longValue(),shortValue()。以上各函数不接受任何的参数)
equals()方法判断number对象是否与参数相等。
== 它比较的是对象的地址
equals 比较的是对象的内容
valueOf()方法返回一个 Number 对象指定的内置数据类型
Integer x = 5;
x.byteValue() #拆箱,包装类转原生类
Integer.valueOf(128); #装箱,原生类转包装类
Math.cos(0) #0度的余弦值
Math.PI
Character ch = new Character('a');
String site = "www.runoob.com";
int len = site.length();
string1.concat(string2); #连接字符串
"Hello," + " runoob" + "!" #连接字符串
System.out.printf("%s,%d",变量1,变量2)
String fs = String.format("%s,%d",变量1,变量2)
StringBuffer sBuffer = new StringBuffer("菜鸟教程官网:");
sBuffer.append("www"); #append()方法 效果类似字符串的拼接
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。
Java语言为内置数据类型char提供了包装类Character类。Character类属于 java.lang 包
String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。
String 类的一个访问器方法是 length() 方法,它返回字符串对象包含的字符数。
length() 方法,length 属性和 size() 方法的区别:
- length() 方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
- length 属性是针对 Java 中的数组来说的,要求数组的长度可以用其 length 属性;
- Java 中的 size() 方法是针对泛型集合说的, 如果想看这个泛型有多少个元素, 就调用此方法来查看!
Java:String、StringBuffer 和 StringBuilder 的区别:
String:字符串常量,字符串长度不可变。Java中String 是immutable(不可变)的。用于存放字符的数组被声明为final的,因此只能赋值一次,不可再更改。String 是被 final 修饰的,他的长度是不可变的。
StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 线程安全的可变字符序列。在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。
StringBuilder:字符串变量(非线程安全)。在内部 StringBuilder 对象被当作是一个包含字符序列的变长数组。
基本原则:
- 如果要操作少量的数据用 String ;
- 单线程操作大量数据用StringBuilder ;
- 多线程操作大量数据,用StringBuffer。
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
#创建字符串
greeting = new String("hello");
String greeting = "hello";
char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};
String helloString = new String(helloArray);
java数组
Java 语言中提供的数组是用来存储固定大小的同类型元素。
数组可以作为参数传递给方法。
数组作为函数的返回值。
#创建数组
int[] intarray = new int[5];
int[] intarray = {1, 2, 3, 4, 5};
int[] intarray = new int[]{1, 2, 3, 4, 5};
#多维数组
String str[][] = new String[3][4];
int a[][] = new int[2][3];
a[0] = new int[2];
a[1] = new int[2];
a[0][0] = 1;
a[0][1] = 2;
a[1][0] = 3;
a[1][1] = 4;
#排序
import java.util.Arrays;
Arrays.sort(a);
java Date类、Calendar类、SimpleDateFormat类、Thread类
java.util 包提供了 Date 类来封装当前的日期和时间。
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Thread;
import java.util.Calendar;
// 初始化 Date 对象
Date date = new Date();
// 使用 toString() 函数显示日期时间
System.out.println(date.toString());
String sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)
Date dt = new SimpleDateFormat("yyyy-MM-dd").parse("2018-06-23") #字符串转Date类
Thread.sleep(1000*3); // 休眠3秒
Calendar c = Calendar.getInstance();//默认是当前日期
c.setTime(dt);
c.add(Calendar.DATE, 10); #把c1对象的日期加上10,也就是c1也就表示为10天后的日期
c.add(Calendar.DATE, -10); #把c1对象的日期减去10,也就是c1也就表示为10天前的日期
Date date = c.getTime();
// 获得年份
int year = c1.get(Calendar.YEAR);
// 获得月份
int month = c1.get(Calendar.MONTH) + 1;
// 获得日期
int date = c1.get(Calendar.DATE);
// 获得小时
int hour = c1.get(Calendar.HOUR_OF_DAY);
// 获得分钟
int minute = c1.get(Calendar.MINUTE);
// 获得秒
int second = c1.get(Calendar.SECOND);
java正则表达式
java.util.regex 包主要包括以下三个类:
- Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。 - Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。 - PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
import java.util.regex.Pattern;
boolean isMatch = Pattern.matches(pattern, content);#pattern是正则表达式,content是待匹配的字符串,返回true或false
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(content);
m.find()
m.group(int) #返回匹配到的字符串
m.matches() #若pattern可以匹配整个字符串则返回true
m.lookingAt() #若pattern从一开始就能匹配就返回true
m.replaceAll(replacestring)#替换所有匹配到的内容
java方法
main 方法是被 JVM 调用的。
一个类的两个方法拥有相同的名字,但是有不同的参数列表,这叫做方法重载,重载的方法必须拥有不同的参数列表。
当一个对象被创建时候,构造方法用来初始化该对象。构造方法和它所在类的名字相同,但构造方法没有返回值。
不管你是否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个默认构造方法,它把所有成员初始化为0。
一旦你定义了自己的构造方法,默认构造方法就会失效。
Java 允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象。
#语法
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
System.out.println() #这句话的用法是调用系统类 System 中的标准输出对象 out 中的方法 println()。
typeName... parameterName #可变参数语法,一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
java Stream、File、IO
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();#读取控制台的输入
System.out.println()
System.out.print()
System.out.write()#输出到控制台
#读取文件的两种方式
InputStream f = new FileInputStream("C:/java/hello");
File f = new File("C:/java/hello");
InputStream is = new FileInputStream(f);
is.read()
#写入数据到文件的两种方式
OutputStream f = new FileOutputStream("C:/java/hello");
File f = new File("C:/java/hello");
OutputStream os = new FileOutputStream(f);
os.write("字节数据")
File类的几种常用方法
File f = new File("C:/java/hello");
f.getName()
f.exists()
f.isDirectory()
f.isFile()
f.length() #文件的长度
f.delete()
f.listFiles()#列出该目录下所有的文件和文件夹
f.mkdirs()
f.toString()
FileWriter类#FileWriter 类从 OutputStreamWriter 类继承而来。该类按字符向流中写入数据。
FileWriter fw = new FileWriter(File file, boolean append)
FileWriter fw = new FileWriter("file_path")
fw.write("写入数据")
fw.close()
FileReader类#FileReader类从InputStreamReader类继承而来。该类按字符读取流中数据。
FileReader fr = new FileReader(File file)
FileReader fr = new FileReader("file_path", boolean append)
char[] c = new char[(int) f.length()];
fr.read(c);
for(char i: c)
System.out.print(i);
fr.close();
总结:
以InputStream(输入)/ OutputStream(输出)为后缀的是字节流;
以Reader(输入)/ Writer(输出)为后缀的是字符流。
#I/O流的典型使用方式
#读文件
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuffer sb = new StringBuffer();
while ((s = in.readLine()) != null)
sb.append(s + "\n");
in.close();
return sb.toString();
}
#写文件
public static void write(String filename) throws IOException {
BufferedReader bf = new BufferedReader(new StringReader("abcdef\nabcdef"));
//PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
PrintWriter pw = new PrintWriter(filename); //简写方式,仍旧是在进行缓存
String s;
while ((s = bf.readLine()) != null) {
//pw.write(s + "\n");
pw.println(s); //自动换行
}
pw.close();
}
#标准输入
public static void stdIn() throws IOException {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String s;
while ((s = stdin.readLine()) != null && s.length() != 0)
System.out.println(s);
}
#标准输出
public static void stdOut() throws IOException {
PrintWriter pw = new PrintWriter(System.out, true);
pw.println("hello world");
}
java Scanner类
// 从键盘接收数据
Scanner scan = new Scanner(System.in);
// 判断是否还有输入
if (scan.hasNextLine()) {
String str2 = scan.nextLine(); // nextLine方式接收字符串
System.out.println("输入的数据为:" + str2);
}
scan.close();
java异常处理
所有的异常类是从 java.lang.Exception 类继承的子类。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
自定义异常:
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
throws 用于方法上,可抛出多个异常,每个异常的类名用逗号隔开。
throw 用在代码块中,后面跟着异常的对象,该对象可以是自定义异常,且 throw 使用在方法中。
public void test() throws Exception {
throw new Exception();
}
java继承
维护性提高,代码也更加简洁,提高代码的复用性。
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
继承的特性:
- 子类拥有父类非private的属性,方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
super 与 this 关键字:
- super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。java 中若要在子类调用父类的方法,需使用关键字super。super 语句必须是子类构造方法的第一条语句。不能在子类中使用父类构造方法名来调用父类构造方法。 父类的构造方法不被子类继承。调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super(); 作为子类构造方法的第一条语句。这会形成一个构造方法链。静态方法中不能使用 super 关键字。如果子类覆盖或重写了父类的方法,则只有使用 super 才能在子类中调用父类中的被重写的方法。java文件被编译成class文件时,在子类的所有构造函数中的第一行(第一个语句)会默认自动添加 super() 语句,在执行子类的构造函数前,总是会先执行父类中的构造函数。子类的所有构造方法内部, 第一行会(隐式)自动先调用父类的无参构造函数super();如果子类构造方法第一行显式调用了父类构造方法,系统就不再调用无参的super()了。
super 表示使用它的类的父类。super 可用于:
调用父类的构造方法;(super();或者super(参数列表);)
调用父类的方法(子类覆盖了父类的方法时);(super.方法名(参数列表);)
访问父类的数据域(可以这样用但没有必要这样用)。 - this关键字:指向自己的引用。
this 关键字表示当前对象。可用于:
调用当前类的构造方法,并且必须是方法的第一条语句。如:this(); 调用默认构造方法。this(参数); 调用带参构造方法。
限定当前对象的数据域变量。一般用于方法内的局部变量与对象的数据域变量同名的情况。如 this.num = num。this.num 表示当前对象的数据域变量 num,而 num 表示方法中的局部变量。
final关键字:
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写,实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。
构造器:
子类不能继承父类的构造器(构造方法或者构造函数),如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
java重写与重载
没有override关键字,这和其他语言有区别。
方法的重写规则:
- 参数列表必须完全与被重写方法的相同;
- 返回类型必须完全与被重写方法的返回类型相同;
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
- 父类的成员方法只能被它的子类重写。
*声明为final的方法不能被重写。 - 声明为static的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个方法,则不能重写这个方法。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
重写与重载之间的区别:
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
java多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象(Parent p = new Child();)(当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。)
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
多态的实现方式:
- 重写
- 接口
- 抽象类和抽象方法
java抽象类与抽象方法
关键字:abstract
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
抽象类总结规定
- 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
- 构造方法,类方法(用static修饰的方法)不能声明为抽象方法。
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
java封装
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
实现java封装的步骤:
- 修改属性的可见性来限制对属性的访问(一般限制为private)。
- 对每个值属性提供对外的公共方法(getter,setter)访问,也就是创建一对赋取值方法,用于对私有属性的访问。
class a{
private int age;
public void setAge(int newAge){
age = newAge;
}
public int getAge(){
return age;
}
}
java接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性:
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
java包
package pkg1[.pkg2[.pkg3…]];
import package1[.package2…].(classname|*);
包的作用
- 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
- 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
java数据结构
Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:
- 枚举(Enumeration)
- 位集合(BitSet)
- 向量(Vector)
- 栈(Stack)
- 字典(Dictionary)
- 哈希表(Hashtable)
- 属性(Properties)
import java.util.Enumeration;
import java.util.Vector;
import java.util.BitSet;
import java.util.Stack;
#Enumeration
Enumeration<String> days;
#boolean hasMoreElements( )
#测试此枚举是否包含更多的元素。
#Object nextElement( )
#如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
while (days.hasMoreElements()){
System.out.println(days.nextElement());
}
#Vector类实现了一个动态数组。
#Vector是同步访问的。
Vector类支持4种构造方法。
第一种构造方法创建一个默认的向量,默认大小为10:Vector()
第二种构造方法创建指定大小的向量:Vector(int size)
第三种构造方法创建指定大小的向量,并且增量用incr指定. 增量表示向量每次增加的元素数目: Vector(int size,int incr)
第四种构造方法创建一个包含集合c元素的向量:Vector(Collection c)
#栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
boolean empty()
测试堆栈是否为空。
Object peek( )
查看堆栈顶部的对象,但不从堆栈中移除它。
Object pop( )
移除堆栈顶部的对象,并作为此函数的值返回该对象。
Object push(Object element)
把项压入堆栈顶部。
#Dictionary 类是一个抽象类,用来存储键/值对
#Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。
#支持同步。
#Hashtable在哈希表中存储键/值对。
#Properties 继承于 Hashtable.表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。
#Properties类定义了两个构造方法. 第一个构造方法没有默认值: Properties()
#第二个构造方法使用propDefault 作为默认值。两种情况下,属性列表都为空:Properties(Properties propDefault)
java集合框架
Collection 接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
Collection 接口存储一组不唯一,无序的对象。List 接口
List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
List 接口存储一组不唯一,有序(插入顺序)的对象。Set
Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。
Set 接口存储一组唯一,无序的对象。SortedSet
继承于Set保存有序的集合。Map
Map 接口存储一组键值对象,提供key(键)到value(值)的映射。Map.Entry
描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。SortedMap
继承于 Map,使 Key 保持在升序排列。
Set和List的区别
Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
集合实现类(集合类):
- LinkedList
该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,LinkedList 查找效率低。 - ArrayList
该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。 - HashSet
该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。 - TreeSet
该类实现了Set接口,可以实现排序等功能。 - HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。 - TreeMap
继承了AbstractMap,并且使用一颗树。
List<String> list=new ArrayList<String>();
list.add("Hello");#添加元素
String[] strArray=new String[list.size()];
list.toArray(strArray);
Iterator<String> ite=list.iterator();
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");#添加元素
map.keySet()#得到所有key
map.values()
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
java泛型
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
public static < E > void printArray( E[] inputArray ){方法体} #泛型方法
public static <T extends Comparable<T>> T maximum(T x, T y, T z){}#上界
public class Box<T> {}#泛型类
public interface Generator<T> {T next();} #泛型接口
public static void getData(List<?> data) {}#类型通配符
<? extends T>和<? super T>的区别:
- <? extends T>表示该通配符所代表的类型是T类型的子类。
- <? super T>表示该通配符所代表的类型是T类型的父类。
java序列化
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
一个类的对象要想序列化成功,必须满足两个条件:
- 该类必须实现 java.io.Serializable 对象。(public class Employee implements java.io.Serializable)
- 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。(public transient int SSN;)
java注释
Java 支持三种注释方式。前两种分别是 // 和 /* /,第三种被称作说明注释,它以 /* 开始,以 /结束。
说明注释允许你在程序中嵌入关于程序的信息。你可以使用 javadoc 工具软件来生成信息,并输出到HTML文件中。
说明注释,使你更加方便的记录你的程序信息。
在开始的 /* 之后,第一行或几行是关于类、变量和方法的主要描述。
之后,你可以包含一个或多个何种各样的 @ 标签。每一个 @ 标签必须在一个新行的开始或者在一行的开始紧跟星号(*).
多个相同类型的标签应该放成一组。例如,如果你有三个 @see 标签,可以将它们一个接一个的放在一起。
Java枚举类型
enum关键字
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
Spiciness howHot = Spiciness.MEDIUM;
System.out.println(howHot); //output: MEDIUM
Spiciness.values(); //output: {NOT, MILD, MEDIUM, HOT, FLAMING},返回一个数组
howHot.ordinal(); // 2, 返回对应的下标,下标从0开始。
java 并发
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程状态图:
线程共包括以下 5 种状态:
新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Java 提供了三种创建线程的方法:
- 实现Runnable接口,重写run();
- 继承Thread抽象类,重写run();
- 实现Callable接口,重写call(),并且有返回值。
class ThreadDemo extends Thread {
ThreadDemo() {
System.out.println("Creating " + Thread.currentThread().getName());
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName() );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + Thread.currentThread().getName() + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + Thread.currentThread().getName() + " interrupted.");
}
System.out.println("Thread " + Thread.currentThread().getName() + " exiting.");
}
}
class RunnableDemo implements Runnable {
RunnableDemo() {
System.out.println("Creating " + Thread.currentThread().getName());
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName() );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + Thread.currentThread().getName() + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + Thread.currentThread().getName() + " interrupted.");
}
System.out.println("Thread " + Thread.currentThread().getName() + " exiting.");
}
}
class CallableDemo implements Callable<String> {
int time;
public CallableDemo(int time ) {
this.time = time;
}
public String call() throws InterruptedException {
List<Integer> list = new ArrayList<>();
for (int i = 1; i < time / 5; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
Thread.sleep(time);
list.add(i);
}
return list.toString();
}
}
class ThreadPoolDemo implements Callable<Integer> {
private int x;
private int y;
public ThreadPoolDemo(int x, int y) {
this.x = x;
this.y = y;
}
public Integer call() throws InterruptedException {
for (int i = 1; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
Thread.sleep(50);
}
return x + y;
}
}
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//
for (int i = 1; i < 3; i++) {
Thread t1 = new Thread(new ThreadDemo(), "Thread-" + i);
System.out.println("Starting " + Thread.currentThread().getName());
t1.start();
//t1.join();
}
//
for (int i = 1; i < 3; i++) {
Thread t1 = new Thread(new RunnableDemo(), "Thread-" + i);
System.out.println("Starting " + Thread.currentThread().getName());
t1.start();
//t1.join();
}
//
for (int i = 1; i < 3; i++) {
CallableDemo cd = new CallableDemo(50 / i);
FutureTask<String> ft = new FutureTask<>(cd);
Thread t = new Thread(ft, "Thread-" + i);
t.start();
System.out.println(ft.get()); //有问题,添加这一行,就变成串行了。
}
//
ExecutorService threadPool = Executors.newFixedThreadPool(2); //创建有2个线程的线程池
for (int i = 1; i < 6; i++) {
ThreadPoolDemo ed = new ThreadPoolDemo(i, i);
Future<Integer> result = threadPool.submit(ed);
System.out.println(result.get()); //有问题,添加这一行,就变成串行了。
}
threadPool.shutdown(); //关闭线程池,不然程序不会终止
}
}