Java特点:
- 一次编写,到处运行,兼容各种不同体系结构的CPU、操作系统
- Java是一种面向对象的语言
- 将源代码编译成字节码(bytecode,中间态)
- 然后依赖各种不同平台上的虚拟机来解释执行字节码
- 新的语言特性能够接纳、兼容和运行其他程序语言产生的字节码
JDK(Java Development Kit)
- 编译:利用JDK中的javac.exe,将.java文件(代码文件)编译成.class文件(字节码文件)
- 运行:基于JRE中的java.exe读入并解释.class文件,最终在JVM上运行
Java组件的关系(Eclipse IDE和Java(JDK、JRE、JVM)的关系)
程序编写遵循的基本编码标准
- 类名首字母大写,后面单词驼峰命名
- 方法名和变量名首字母小写,后面单词驼峰命名
Class类
- Java文件必须以.java作为扩展名
- 一个Java文件只能有一个public class(其名字必须和文件名字一样,大小写都要完全一致)
- 一个文件可以有多个class,但只能有一个是public,不提倡一个文件里面放多个类(内部类除外)
基本数据类型
- boolean:只有true和false两种取值,默认false
- byte:1byte=8bits,(-128~127),默认值0
- short:16位,(-32768 ~32767 ,-2^15~ 2 ^15-1),默认值0( 注意: 在对变量赋值的时候不能超过变量数据类型的取值范围,不然会报错)
- int:32位(-2147483648~ 2147483647,-2^31~ 2 ^31-1),默认值0
- long:64位(-9223372036854775808 ~ 9223372036854775807,-2^63~ 2 ^63-1),默认值0L( 注意:对long类型的变量赋值的时候,后面尽量加L。如果不加L,那么默认为是int转换为long,如果该值超过了int的取值范围,也会报错,但加了L就不会 )
- float:32位,符合IEEE753标准的浮点数(1.4E-45~ 3.4028235E38),默认值0.0f(注意:赋值后面必须带f,如float f1=1.23f )
- double:64位,符合IEEE753标准的浮点数(4.9E-324d~1.7976931348623157E308d),默认值0.0d(注意:可以省略末尾d )
- char:一个单一的16位Unicode字符,最小值\u0000(0),最大值\uffff(65535),可以存储任何字符,包括中文。
自定义函数
- 形式:修饰词(public或static) 返回值 函数名 (形参列表){函数体}
- 同一个类中,函数名称可以相同,即重载函数,但是函数参数的个数或者类型必须有所不同,不能以返回值来区分同名的函数
构造函数
- Java构造函数的名称必须和类名一样,且没有返回值
- 如果没有显式定义构造函数,Java编译器自动为该类产生一个空的无形参构造函数,如果有显式的构造函数,编译器就不会产生了
- 每个子类的构造函数的第一句话都默认调用父类的无参构造函数super(),除非子类的构造函数第一句话是super。super语句必须放在第一条,不能连续出现两条super语句
继承
- 每个类只能继承一个类
- 如果不写extends,Java类默认继承java.lang.Object类
抽象类(abstract class)
- 不能new操作
- 如果一个类暂时有方法未实现,需要被定义为抽象类
- 组成:成员变量(个数不限)、具体方法(个数不限)、抽象方法(加abstract关键字,个数不限)
- 抽象类也是类。一个类继承于抽象类就不能继承于其他的(抽象)类
- 子类可以继承抽象类,但一定要实现父类们所有abstract方法,如果不能完全实现,那么子类也必须被定义为抽象类
- 有构造函数
- 可以有main函数,也能运行
- 方法可以有private/protected
接口(interface)
- 不能new操作
- 如果类的所有方法都没有实现,那么这个类就算是接口
- 类可以实现(implements)多个接口,继承和实现可以同时
- 接口可以继承多个接口(注意:是继承,用extends,接口之间用逗号隔开,如public interface Catfamily extends Animal,ClimbTree{ },Animal,ClimbTree都是接口 ),没有实现的方法将叠加
- 类实现接口,就必须实现所有未实现的方法。如果没有全部实现,那么只能成为一个抽象类
- 接口里可以定义变量,但是一般是常量
- 没有构造函数
- 没有main函数
- 方法都是public
类转型
- 变量支持相互转换
- 类型可以相互转换,但是只限制于有继承关系的类(子类可以转换成父类,而父类不可以转为子类,除非这个父类本身就是从子类转化过来的)
多态
- 类型转化带来的作用就是多态(以统一的接口来操纵某一类中不同的对象的动态行为)
- 子类转型为父类后,调用普通方法,依旧是子类的方法
- 注意重载和重写的区别
- 子类的方法优先级高于父类
static
- static关键字可以用在变量、方法、类、匿名方法块
- static变量只依赖于类存在(通过类即可访问),不依赖于对象实例存在,所有的对象实例,它们的静态成员变量共享存储在一个共同的空间。
- static方法也可以通过类名直接引用。但是静态方法中,只能使用静态变量,不能使用非静态变量。而且静态方法禁止引用非静态方法。
- static块只在类第一次被加载时调用,在程序运行期间,这段代码只运行一次
- 执行顺序:static块>匿名块(用{}括起来的一段代码)>构造函数
单例模式(Singleton)
- 又名单态模式
- 限定某个类在整个程序运行过程中,只能保留一个实例对象在内存空间
- 采用static来共享对象实例
- 采用private构造函数,防止外界new操作
final
- final关键字可以用来修饰类、方法、字段
- final的类,不能被继承
- 父类中如果有final的方法,子类中不能改写此方法
- final变量,基本类型不能修改值。对象类型不能修改指针,但能修改对象内部的值
常量
- Java没有const关键字
- 定义常量用public static final
- 接口内定义的变量默认是常量(自动加上public static final)
- Java为很多基本类型的包装类/字符串都建立常量池(相同的值只存储一份,节省内存,共享访问)
- 基本类型的包装类和字符串有两种创建方式
1、常量式赋值创建,放在栈内存(将被常量化)
2、new对象进行创建,放在堆内存(不会常量化) - 以下基本类型的包装类会建立常量池
1、 Boolean:true,false
2、Character:0~127
3、Byte、Short、Int、Long:-128~127
public class BoxClassTest {
public static void main(String[] args)
{
int i1 = 10;
Integer i2 = 10; // 自动装箱
System.out.println(i1 == i2); //true
// 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
Integer i3 = new Integer(10);
System.out.println(i1 == i3); //true
// 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
System.out.println(i2 == i3); //false
// 两个对象比较,比较其地址。
// i2是常量,放在栈内存常量池中,i3是new出对象,放在堆内存中
Integer i4 = new Integer(5);
Integer i5 = new Integer(5);
System.out.println(i1 == (i4+i5)); //true
System.out.println(i2 == (i4+i5)); //true
System.out.println(i3 == (i4+i5)); //true
// i4+i5 操作将会使得i4,i5自动拆箱为基本类型并运算得到10.
// 基础类型10和对象比较, 将会使对象自动拆箱,做基本类型比较
Integer i6 = i4 + i5; // +操作使得i4,i5自动拆箱,得到10,因此i6 == i2.
System.out.println(i1 == i6); //true
System.out.println(i2 == i6); //true
System.out.println(i3 == i6); //false
}
}
- Java为常量字符串都建立常量池缓存机制
public class StringConstantTest {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = "ab" + "c"; //都是常量,编译器将优化,下同
String s4 = "a" + "b" + "c";
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //true
System.out.println(s1 == s4); //true
}
}
public class StringNewTest {
public static void main(String[] args) {
String s0 = "abcdef";
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2); //true 常量池
System.out.println(s1 == s3); //false 一个栈内存,一个堆内存
System.out.println(s3 == s4); //false 两个都是堆内存
System.out.println("=========================");
String s5 = s1 + "def"; //涉及到变量,故编译器不优化
String s6 = "abc" + "def"; //都是常量 编译器会自动优化成abcdef
String s7 = "abc" + new String ("def");//涉及到new对象,编译器不优化
System.out.println(s5 == s6); //false
System.out.println(s5 == s7); //false
System.out.println(s6 == s7); //false
System.out.println(s0 == s6); //true
System.out.println("=========================");
String s8 = s3 + "def";//涉及到new对象,编译器不优化
String s9 = s4 + "def";//涉及到new对象,编译器不优化
String s10 = s3 + new String("def");//涉及到new对象,编译器不优化
System.out.println(s8 == s9); //false
System.out.println(s8 == s10); //false
System.out.println(s9 == s10); //false
}
}
不可变对象
- 一旦创建,这个对象(状态/值)不能被更改了
- 其内在的成员变量的值就不能修改了
- 典型的不可变对象
1、八个基本型别的包装类的对象
2、String、BigInteger和BigDecimal等的对象 - 不可变对象,也是传指针(由于不可变,临时变量指向新内存,外部实参的指针不改动)
public class ImmutableObjectTest {
public static void main(String[] args) {
String a = new String("abc");
String b = a;//b和a指向同一块内存
System.out.println(b);//abc
a = "def";//新开一块内存,其值为“def”,a指向这块内存,b指向的内存不变
System.out.println(b);//abc
a=new String("abc");
change(a);
System.out.println(a);//abc
}
public static void change(String b)
{
b = "def";
}
}
- 如何创建不可变对象
1、immutable对象是不可改变的,有改变,需clone/new一个对象进行修改
2、所有的属性都是final和private的
3、不提供set方法
4、类是final的,或者所有的方法都是final的 - 优点:只读,线程安全,并发读,提高性能,可以重复使用
- 缺点:制造垃圾,浪费空间(因为每一次修改指针的值,都是新开一块内存,让指针指向新开的内存,原来的那块内存只能等待垃圾回收)
- String是不可变对象,但是StringBuffer(同步(只能有一个线程修改),线程安全,修改快速)/StringBuilder(不同步(可以两个线程同时修改),线程不安全,修改更快)类都是可变对象,可以用append方法进行修改
public class ArgumentPassing {
public static void changeValue(int a)
{
a = 10;
}
public static void changeValue(String s1)
{
s1 = "def";
}
public static void changeValue(StringBuffer s1)
{
s1.append("def");
}
public static void main(String[] args) {
int a = 5; //基本类型
String b = "abc"; //不可变对象
StringBuffer c = new StringBuffer("abc"); //可变对象
changeValue(a);
changeValue(b);
changeValue(c);
System.out.println(a);//5
System.out.println(b);//abc
System.out.println(c);//abcdef
}
}
String类
public class StringTest {
public static void main(String[] args) {
String a = "123;456;789;123 ";
System.out.println(a.charAt(0)); // 返回第0个元素
System.out.println(a.indexOf(";")); // 返回第一个;的位置
System.out.println(a.concat(";000")); // 连接一个新字符串并返回,a不变
System.out.println(a.contains("000")); // 判断a是否包含000
System.out.println(a.endsWith("000")); // 判断a是否以000结尾
System.out.println(a.equals("000")); // 判断是否等于000
System.out.println(a.equalsIgnoreCase("000"));// 判断在忽略大小写情况下是否等于000
System.out.println(a.length()); // 返回a长度
System.out.println(a.trim()); // 返回a去除前后空格后的字符串,a不变
String[] b = a.split(";"); // 将a字符串按照;分割成数组
for (int i = 0; i < b.length; i++) {
System.out.println(b[i]);
}
System.out.println("===================");
System.out.println(a.substring(2, 5)); // [2,5) 截取a的第2个到第5个字符 a不变
System.out.println(a.replace("1", "a"));
System.out.println(a.replaceAll("1", "a")); // replaceAll第一个参数是正则表达式,a不变
System.out.println("===================");
String s1 = "12345?6789";
String s2 = s1.replace("?", "a");
String s3 = s1.replaceAll("[?]", "a");
// 这里的[?] 才表示字符问号,这样才能正常替换。不然在正则中会有特殊的意义就会报异常
System.out.println(s2);
System.out.println(s3);
System.out.println(s1.replaceAll("[\\d]", "a")); //将s1内所有数字替换为a并输出,s1的值未改变。
}
}
StringBuffer类
public class StringBufferCapacityTest {
public static void main(String[] args) {
//StringBuffer的的初始大小为(16+初始字符串长度)即capacity=16+初始字符串长度
//length 实际长度 capacity 存储空间大小
StringBuffer sb1 = new StringBuffer();
System.out.println("sb1 length: " + sb1.length());//0
System.out.println("sb1 capacity: " + sb1.capacity());//16
System.out.println("=====================");
StringBuffer sb2 = new StringBuffer("123");
sb2.append("456");
System.out.println("sb2 length: " + sb2.length());//6
System.out.println("sb2 capacity: " + sb2.capacity());//19
System.out.println("=====================");
sb2.append("7890123456789");
System.out.println("sb2 length: " + sb2.length());//19
System.out.println("sb2 capacity: " + sb2.capacity());//19
System.out.println("=====================");
sb2.append("0");
System.out.println("sb2 length: " + sb2.length());//20
System.out.println("sb2 capacity: " + sb2.capacity());//40
//一旦length大于capacity时,capacity便在前一次的基础上加1后翻倍;
System.out.println("=====================");
//当前sb2length 20 capacity 40, 再append 70个字符 超过(加1再2倍数额)
sb2.append("1234567890123456789012345678901234567890123456789012345678901234567890");
System.out.println("sb2 length: " + sb2.length());//90
System.out.println("sb2 capacity: " + sb2.capacity());//90
//如果append的对象很长,超过(加1再2倍数额),将以最新的长度更换
System.out.println("=====================");
sb2.append("0");
System.out.println("sb2 length: " + sb2.length());//91
System.out.println("sb2 capacity: " + sb2.capacity());//182
sb2.trimToSize();//去除空隙,将字符串存储压缩到实际大小
System.out.println("=====after trime================");
System.out.println("sb2 length: " + sb2.length());//91
System.out.println("sb2 capacity: " + sb2.capacity());//91
}
}
package
- 如果所有的Java类都是放置在同一个目录(包)下面,那么类之间的相互调用无需显式声明调用(同一目录下面,两个类的名字不能相同)
- Java支持多个目录放置Java,并通过package/import/classpath/jar等机制配合使用,可以支持跨目录放置和调用Java类
- 类的完整的名字:包名+类名
- 如果使用默认包,那么类文件前面可以不写包名,否则,需要写包名
import
- import必须放在package之后,类定义之前
- 可以用*来引入一个目录下的所有类,但不包括该目录下面子目录中的文件
- 如果被调用的类不在调用类所在的包中,那么需要用import引入
- 注意:*引入可能出现同名文件
jar
- jar文件实际上是一组class文件的压缩包
- jar文件只包括class,而没有java文件
- 导出的jar文件并不是放在同一个目录下,而是根据导出时文件的相对存放位置存放的,在引用其中的class文件时,只需要import +jar文件中class路径即可,不需要写jar文件的路径和名称了
Java访问权限
- private:私有的,只能本类访问
- default:通常不写,同一个包内访问
- protected:同一个包,不同包的子类能访问(子类继承父类之后,直接使用,而不是在子类中new一个父类,然后通过该对象调用)
- public:公开的,所有类都可以访问
- 四种权限都可以修饰成员变量、成员方法、构造函数
- default和public可以修饰类
Java数字类(java.math包)
- 整数:Short、Int、Long
- 浮点数:Float、Double
- 大数类:BigInteger(支持无限大的整数运算)、BigDecimal(支持无限大的小数运算,注意精度和截断)
import java.math.BigInteger;
public class BigIntegerTest {
public static void main(String[] args) {
BigInteger b1 = new BigInteger("123456789"); // 声明BigInteger对象
BigInteger b2 = new BigInteger("987654321"); // 声明BigInteger对象
System.out.println("b1: " + b1 + ", b2:" + b2);
System.out.println("加法操作:" + b2.add(b1)); // 加法操作
System.out.println("减法操作:" + b2.subtract(b1)); // 减法操作
System.out.println("乘法操作:" + b2.multiply(b1)); // 乘法操作
System.out.println("除法操作:" + b2.divide(b1)); // 除法操作
System.out.println("最大数:" + b2.max(b1)); // 求出最大数
System.out.println("最小数:" + b2.min(b1)); // 求出最小数
BigInteger result[] = b2.divideAndRemainder(b1); // 求出余数的除法操作
System.out.println("商是:" + result[0] + ";余数是:" + result[1]);
System.out.println("等价性是:" + b1.equals(b2));
int flag = b1.compareTo(b2);
if (flag == -1)
System.out.println("比较操作: b1<b2");
else if (flag == 0)
System.out.println("比较操作: b1==b2");
else
System.out.println("比较操作: b1>b2");
}
}
import java.math.BigDecimal;
import java.math.BigInteger;
public class BigDecimalTest {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("123456789.987654321"); // 声明BigDecimal对象
BigDecimal b2 = new BigDecimal("987654321.123456789"); // 声明BigDecimal对象
System.out.println("b1: " + b1 + ", b2:" + b2);
System.out.println("加法操作:" + b2.add(b1)); // 加法操作
System.out.println("减法操作:" + b2.subtract(b1)); // 减法操作
System.out.println("乘法操作:" + b2.multiply(b1)); // 乘法操作
//需要指定位数,防止无限循环,或者包含在try-catch中
System.out.println("除法操作:" + b2.divide(b1,10,BigDecimal.ROUND_HALF_UP)); // 除法操作,10为保留小数位数,BigDecimal.ROUND_HALF_UP四舍五入
System.out.println("最大数:" + b2.max(b1)); // 求出最大数
System.out.println("最小数:" + b2.min(b1)); // 求出最小数
int flag = b1.compareTo(b2);
if (flag == -1)
System.out.println("比较操作: b1<b2");
else if (flag == 0)
System.out.println("比较操作: b1==b2");
else
System.out.println("比较操作: b1>b2");
System.out.println("===================");
//尽量采用字符串赋值
System.out.println(new BigDecimal("2.3"));//2.3
System.out.println(new BigDecimal(2.3));//2.29999999999999982236431605997495353221893310546875
System.out.println("===================");
BigDecimal num1 = new BigDecimal("10");
BigDecimal num2 = new BigDecimal("3");
//需要指定位数,防止无限循环,或者包含在try-catch中
BigDecimal num3 = num1.divide(num2, 3, BigDecimal.ROUND_HALF_UP);
System.out.println(num3);
}
}
- 随机数类:Random
1、nextInt()//返回一个随机int
2、nextInt(int a)//返回一个[ 0 , a )之间的随机 int
3、nextDouble()//返回一个[ 0.0 ,1.0 ]之间的double
4、ints方法批量返回随机数数组 - Math.random()//返回一个[ 0.0 ,1.0 ]之间的double
- 工具类:Math
import java.util.Random;
public class RandomTest {
public static void main(String[] args)
{
//第一种办法,采用Random类 随机生成在int范围内的随机数
Random rd = new Random();
System.out.println(rd.nextInt());
System.out.println(rd.nextInt(100)); //0--100的随机数
System.out.println(rd.nextLong());
System.out.println(rd.nextDouble());
System.out.println("=========================");
//第二种,生成一个范围内的随机数 例如0到时10之间的随机数
//Math.random[0,1)
System.out.println(Math.round(Math.random()*10));
System.out.println("=========================");
//JDK 8 新增方法
rd.ints(); //返回无限个int类型范围内的数据
int[] arr = rd.ints(10).toArray(); //生成10个int范围类的个数。
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("=========================");
arr = rd.ints(5, 10, 100).toArray();//生成5个[10,100)中的数。
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("=========================");
arr = rd.ints(10).limit(5).toArray();//只有5个数
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
数字工具类java.lang.Math
public class MathTest {
public static void main(String[] args) {
System.out.println(Math.abs(-5)); //绝对值
System.out.println(Math.max(-5,-8)); //最大值
System.out.println(Math.pow(-5,2)); //求幂
System.out.println(Math.round(3.5)); //四舍五入
System.out.println(Math.ceil(3.5)); //向上取整
System.out.println(Math.floor(3.5)); //向下取整
}
}
不熟悉的点
- 子类可以继承父类所有内容(不能直接访问private成员,即父类中有一个private成员,子类继承父类之后,字类的对象不能调用和使用该private成员)
- (A obj=new A(),obj相当于一个指针,java中叫作reference)
- 类中的属性是有默认初值的,但是方法中定义的变量没有,需要进行初始化,不然会报错
- this还能代替本类的构造函数,可以用在一个构造函数里面调用另一个构造函数的时候,如this(5)//调用本类的一个形参的构造函数