面向对象编程
面向对象的概念
OOP:Object Oriented Programming
class 和 instance 是“模板”和“实例”的关系 ==>
类:class,对象模板
实例:instance,具体对象
class用字段(field)封装了数据,不同的instance拥有各自独立的field
通过 **变量.字段名** 访问某个字段
指向 instance 的变量都是引用变量
数据封装
方法
方法封装了访问实例字段的逻辑
实例字段修饰符
public:外部代码可以访问该字段
private:外部代码无法访问该字段
方法修饰符
public:外部代码可以访问该方法
private:外部代码无法访问该方法
方法是一组执行语句,遇到return返回
void表示不返回任何值(注意区分null)
方法命名规范
方法内部的隐私变量this
构造方法Constructor
构造方法可以在new操作时初始化实例对象
构造方法名就是类名
构造方法没有返回值
编译器会为没有定义构造方法的类自动生成默认构造方法(无参数、无执行语句)
如果我们自定义了构造方法,编译器就不再自动创建默认构造方法
初始化顺序:
1)初始化字段(没有赋值的字段初始化为默认值,基本类型=0,引用类型=null)
2)执行构造方法代码
可以定义多个构造方法,编译器根据参数数量、位置和类型自动判断使用哪个
可以在一个构造方法中调用其他构造方法,便于代码的复用,调用方法是this(...)
方法重载 Overload
指方法名相同,单参数不同:
1)类型不同
2)数量不同
3)位置不同
方法重载的目的是方便调用
重载方法应该完成相同的功能,参考String的indexOf()
1)indexOf(String str)
2)indexOf(String str1, int FromIndex)
不要去交换参数顺序
1)indexOf(String str, int fromIndex)
2)indexOf(int fromIndex, String str)
重载方法返回值类型应该相同
继承和多态
继承
继承是面向对象编程的代码复用方式
Java使用extend继承
被继承类:超类,父类,基类 继承类:子类,派生类
Java只允许继承一个类
Object是所有类的根类
protected修饰的字段和方法可以被子类访问,protected把字段和方法的访问权限控制在了继承树内部
super表示父类
没有调用super()时编译器会自动生成super()语句,如果父类没有默认构造方法,子类就必须显示调用super()
向上转型和向下转型 Upcasting Downcasting
子类类型可以安全地向上转型为父类类型
父类类型可以强制向下转型为子类类型(可能报错ClassCastException)
我们在做向下转型的时候,可以先用instanceof判断类型
继承和组合
继承是is关系
组合是has关系
多态Polymorphic
方法覆盖:
子类覆写父类的方法,Override
方法签名如果不同就不是Override,而是创建了一个新方法
加上@Override可以让编译器帮助检查是否进行正确的覆写
@Override不是必需的
多态:
针对某个类型的方法调用,其真正执行的方法取决于运行时实际类型的方法
Java的实例方法调用是基于运行时实际类型的动态调用 ===> 多态
对某个类型调用方法,执行的方法可能是某个子类的覆写方法
允许添加更多类型的子类来扩展功能
Object定义的几个重要的方法:
1)toString:把instance输出为String
2)equals:判断两个instance是否逻辑相等
3)hashCode:计算一个instance的哈希值
final
用final修饰的方法不能被Override
用final修饰的类不能被继承
用final修饰的字段初始化后不能重新赋值
抽象类和接口
抽象类 Abstract Class
抽象方法用abstract修饰,抽象方法没有任何执行语句;因为无法执行抽象方法,所以这个类也必须申明为抽象类(abstract class)
无法实例化一个抽象类
含有抽象方法的类称为抽象类
抽象类的作用:
1)被继承
2)强迫子类实现抽象方法
3)抽象方法相当于定义“规范”
面向抽象编程的本质:
1)上层代码只定义规范
2)不需要子类即可编译
3)具体逻辑由不同子类实现,调用者并不关心
接口 Interface
接口定义了纯抽象规范,使用interface声明一个接口,接口只有抽象方法
实现interface使用implements
一个class可以实现多个interface
接口:
1)不能定义实例字段
2)不能定义普通方法
3)可以定义default方法(JDK>=1.8)
一个接口可以extends另一个接口,相当于扩展接口方法
接口层次代表抽象程度
接口也是数据类型,适用于向上转型和向下转型
abstract class | interface | |
---|---|---|
继承 | 只能extends一个class | 可以implements对个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 |
包和classpath
静态字段和方法
用static修饰的字段:静态字段,属于class,不属于实例
普通字段在每个实例中都有自己的一个独立“空间”
静态字段只有一个共享“空间”,所有实例都共享该字段
访问静态字段:类名.静态字段 不推荐使用 实例变量.静态字段
用static修饰的方法:静态方法,属于class,不属于实例
调用实例方法必须通过实例变量
调用静态方法不需要实例变量
静态方法类似其他编程语言的函数
访问静态方法:类名.静态方法 不推荐使用 实例变量.静态方法
静态方法不能访问this变量,但可以访问静态字段
静态方法不能访问实例字段
静态方法常用用于工具类(Arrays.sort()、Math.random())和辅助方法
Java程序的入口main()也是静态方法
包 Packages
package用于解决类名冲突:
Java完整类名=包名+类名;JVM只看完整类名,编译器编译后的class只含完整类名
包可以有多层结构,包没有父子关系(例如:java.util和java.util.zip是不同的包,两者没有任何关系)
包作用域
位于同一个包的类,可以访问包作用域的字段和方法
不用public、protected、private修饰的字段和方法就是包的作用域
引用其他类的方法
使用完整类名;先import,再使用类名;可以使用*(不推荐)
static import可以导入一个类的静态字段和静态方法,很少使用
查找class
编译器查找class完整类名的步骤:
1)根据完整类名查找
2)查找当前package
3)查找import的class
4)查找java.lang的class
5)编译错误
默认自动import当前package,默认自动import java.lang.*
最佳实践
包名使用倒置域名确保唯一,不要和java.lang的类重名,不用和JDK常用类重名
作用域
访问权限
访问权限指一个类内部,能否引用另一个类以及它的字段和方法
方法权限有public、protected、private和package四种
final不是访问权限
最佳实践:最小化暴露对外方法
局部变量
方法内部定义的变量时局部变量(包括方法参数名)
局部变量作用域由所在语句块{...}决定
classpath和jar
classpath
classpath是环境变量,指示JVM如何搜索class,路径和操作系统相关
假设classpath是 .;C:\work\project1\bin;C:\shared
JVM在加载com.feiyangedu.Hello这个类时,依次查找:
<当前目录>\com\feiyangedu\Hello.class
C:\work\project1\bin\com\feiyangedu\Hello.class
C:\shared\com\feiyangedu\Hello.class
在某个路径下找到了,就不再继续搜索;如果都没有找到,就报错
classpath设置方法:
1)在系统环境变量设置(不推荐)
2)启动JVM时用-classpath或-cp设置(推荐)
java -classpath C:\work\bin;C\shared com.feiyangedu.Hello
java -cp C:\work\bin;C\shared com.feiyangedu.Hello
Eclipse自动传入当前工程bin目录作为classpath
jar
jar包是zip格式的压缩文件,包含若干class文件
jar包相当于目录
使用jar包来避免大量目录和class文件
创建jar包:
1)JDK的jar命令
2)Maven等工具
3)压缩为zip然后改名为jar
jar包可以有一个特殊的/META-INF/MANIFEST.MF文件来指定Main-Class
例如
Mainfest-Version: 1.0
Main-Class: com.feiyangedu.sample.Main
X-Copyright: dafjlaf...
X-Build-Version: 1.0
Java核心类
字符串和编码
字符串
Java字符串的特点:
1)字符串对象可以直接使用 双引号 “...” 表示
2)内容不可变
3)使用equals(Object)判断是否相等,equalsIgnoreCase(String)忽略大小写比较
字符串常用操作:
1)是否包含子字符串:contains、indexOf、lastIndex、startsWith、endsWith
boolean contains(CharSequence)
int indexOf(String)
int lastIndex(String)
boolean startsWith(String)
boolean endsWith(String)
2)去除首尾空白字符:trim
空白字符包括:空格,\t,\r,\n
trim()不改变字符串内容,而是返回新字符串
3)提取子串:substring
4)大小写转换:toUpperCase、toLowerCase
5)替换子串:replace、replaceAll(正则表达式替换)
6)分割:split
String[] split(String)
7)拼接:join
static String join()
String和其他数据类型互相转换
// 把任意数据转换为String
static String valueOf(int)
static String valueOf(boolean)
static String valueOf(Object)
// 把String转换为其他类型
static int Integer.parseInt(String)
static Integer Integer.valueOf(String)
String和char[]互相转换
// String转换为char[]
char[] toCharArray()
// char[]转换为String
new String(char[])
String和byte[]互相转换(需要指定编码)
//String转换为byte[]
byte[] getBytes() // 不推荐,原因是windows上默认是GBK,而在linux上是utf-8
byte[] getBytes(String)
byte[] getBytes(Charset)
// byte[]转换为String
new String(byte[], String)
new String(byte[], Charset)
编码
ASCII:最早的字符编码,一个字符占一个字节,最多表示128个字符
GB2312/GBK/GB18030:一个中文字符占2个字节,第一个字节最高位是1
Unicode:全球统一编码,全球所有文字都有唯一编码,一个Unicode字符通常占用2个字节,Java使用Unicode编码
Q:有了Unicode为什么还需要UTF-8?
A:英文Unicode编码和ASCII不一致,包含大量英文的文本会浪费空间
UTF-8:是变长编码,英文UTF-8编码和ASCII一致,其他Unicode字符需要2~6字节不等,UTF-8编码容错能力强
建议总是使用UTF-8编码
StringBuilder
String可以用+拼接,但是每次循环都会创建新的字符串对象,绝大多数都是临时对象,浪费内存,影响GC效率
可以高效拼接String
是可变对象
可以预分配缓冲区
可以进行链式操作
不要使用StringBuffer
StringBuffer是StringBuilder的线程安全版本,很少使用
包装类型 Wrapper
基本类型 | 对应的包装类型 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
int、Integer和String的相互转换
int i = 100;
Integer n1 = new Integer(i);
Integer n2 = Integer.valueOf(i);
Integer n3 = Integer.valueOf("100");
int x1 = n1.intValue();
int x2 = Integer.parseInt("100");
String s = n1.toString();
// WARNING
Integer prop = Integer.getInteger("cpus");//从系统环境中读取系统变量
自动装箱:auto boxing
int ==> Integer
自动拆箱:auto unboxing
Integer ==> int
自动装箱和自动拆箱只发生在编译阶段,装箱和拆箱会影响执行效率
编译后的class代码是严格区分基本类型和引用类型的
自动拆箱可能发生 NullPointerException
包装类型定义了一些有用的静态变量
整数和浮点数包装类型继承自Number
Boolean t = Boolean.TRUE;
Boolean f = Boolean.FALSE;
int max = Integer.MAX_VALUE; // 2147483647
int min = Integer.MIN_VALUE; // -2147483648
int sizeOfLong = Long.SIZE; // 64(bits)
int bytesOfLong = Long.BYTES; // 8(bytes)
JavaBean
Q:什么是JavaBean?
A:JavaBean是一种Java编程规范,例如
// private Type field
// public Type getField()
// public void setField(Type value)
public class Person {
private String name;
private int age;
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;
}
}
目的:
1)方便IDE工具读写属性
2)传递数据
3)枚举属性
JavaBean的特点:
1)一组public getter/setter方法
2)boolean属性的读方法通常为isXxx()
3)属性可以是只读或只写的
4)属性只需要getter/setter方法,不一定需要实例字段
5)利用IDE可以快速生成属性方法
6)使用introspector.getBeanInfo()获取属性列表
public class Person {
private String name;
private int age;
private boolean gender;
private String address;
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 boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class Main {
public static void main(String[] args) throws Exception{
BeanInfo bInfo = Introspector.getBeanInfo(Person.class);
for(PropertyDescriptor pd : bInfo.getPropertyDescriptors()){
printPropertyDescriptor(pd);
}
}
static void printPropertyDescriptor(PropertyDescriptor pd){
String name = pd.getName();
Class<?> clazz = pd.getPropertyType();
if(clazz == null || name.equals("class")){
return;
}
Method read = pd.getReadMethod();
Method write = pd.getWriteMethod();
System.out.println("===== PropertyDescriptor =====");
System.out.println("Name: " + name);
System.out.println("Type: " + clazz.getName());
System.out.println("Read method: " + (read == null ? "null" : read.getName()));
System.out.println("Write method: " + (write == null ? "null" : write.getName()));
}
}
===== PropertyDescriptor =====
Name: address
Type: java.lang.String
Read method: getAddress
Write method: setAddress
===== PropertyDescriptor =====
Name: age
Type: int
Read method: getAge
Write method: setAge
===== PropertyDescriptor =====
Name: gender
Type: boolean
Read method: isGender
Write method: setGender
===== PropertyDescriptor =====
Name: name
Type: java.lang.String
Read method: getName
Write method: setName
枚举类 Enumeration
Java使用enum定义常量类型,常量本身带有类型信息,可以使用==比较
enum定义的类型是class,继承java.lang.Enum
所有常量都是唯一引用实例
常量可用于switch语句
name()获取常量定义的字符串,注意不要使用toString()
ordinal()返回常量定义的顺序(无实质意义)
可以为enum类编写构造方法、字段、方法
构造方法必须为private
public static void main(String[] args) {
for(Weekday day : Weekday.values()){
System.out.println(day.name());
}
Weekday fri = Weekday.FRI;
// enum -> String:
System.out.println("FRI.name() = " + fri.name());
// 定义时的序号:
System.out.println("FRI.ordinal() = " + fri.ordinal());
// String -> enum:
System.out.println(Weekday.valueOf("FRI").name());
// 不存在的name:
Weekday.valueOf("ABC");
}
SUN
MON
TUE
WED
THU
FRI
SAT
FRI.name() = FRI
FRI.ordinal() = 5
FRI
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant test06.Weekday.ABC
at java.lang.Enum.valueOf(Enum.java:238)
at test06.Weekday.valueOf(Weekday.java:1)
at test06.Main.main(Main.java:46)
public enum Weekday {
SUN("星期日"),
MON("星期一"),
TUE("星期二"),
WED("星期三"),
THU("星期四"),
FRI("星期五"),
SAT("星期六");
private String chinese;
private Weekday(String chinese){
this.chinese = chinese;
}
public String toChinese(){
return chinese;
}
}
public static void main(String[] args) {
Weekday fri = Weekday.FRI;
System.out.println(fri.toChinese()); // 星期五
}
常用工具类
Math:数学计算
1)abs / min / max
2)pow / sqrt / exp / log / log10
3)sin / cos / tan / asin / acos ...
常量: PI = 3.14159... E = 2.71828
Math.random() 生成一个随机数:0 <= 随机数 < 1;可用于生成某个区间的随机数
Random:生成伪随机数
nextInt / nextLong / nextFloat
nextInt(N) 生成不大于N的随机数
什么是伪随机数:
给定种子后伪随机数算法会生成完全相同的序列
不给定种子时Random使用系统当前时间戳作为种子
SecureRandom:生成安全的随机数,缺点是比较慢
BigInteger:表示任意大小的整数
BigDecimal:表示任意精度的浮点数