JavaSE(二)面向对象编程

面向对象的概念

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:表示任意精度的浮点数

猜你喜欢

转载自blog.csdn.net/yangwei234/article/details/84955760