java面向对象编程(高级)
java面向对象编程(高级)
Jinstatic 关键字
如果想让一个成员变量或方法被类的所有实例所共享,就用 static 修饰即可,称为类变量、类方法或类属性、静态变量
使用范围:属性
、方法
、代码块
、内部类
被修饰后具备的特点:
- 随着类的加载而加载
- 优先于对象存在
- 被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
static 成员变量和方法
静态变量
使用 static 修饰的成员变量就是静态变量(或类变量、类属性)
public class Goods{
// 实例变量
String name;
double price;
// 类变量,静态变量
static String brand = "优乐美";
public Goods() {}
public Goods(String name) {
this.name = name;
}
}
public calss GoodsTest{
public static void main(String[] args){
Goods goods1 = new Goods("xm");
Goods goods2 = new Goods("xn");
// 都输出 “优乐美”
System.out.println(goods1.brand);
System.out.println(goods2.brand);
System.out.println(Goods.brand); // 直接类调用
// 改变了,都输出 “哇哈哈”,因为变量的值是共享的
goods1.brand = "哇哈哈";
System.out.println(goods1.brand);
System.out.println(goods2.brand);
System.out.println(Goods.brand); // 直接类调用
}
}
静态变量的特点:
- 默认值规则与实例变量一样
- 静态变量值所有对象共享
- 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用
- 如果权限修饰符允许,在其他类中可以通过
类名.静态变量
直接访问,也可以通过对象.静态变量
的方式访问 - 静态变量的 get/set 方法也静态的,当局部变量与静态变量重名时,使用
类名.静态变量
进行区分
静态方法
用 static 修饰的成员方法就是静态方法
public class Father {
static String name = "Father";
public static void method(){
// 方法内部只能访问类的 static 修饰的属性或方法
System.out.println( name + "method");
}
}
public class Son extends Father {
// @Override
// 尝试重写静态方法,加上@Override 编译报错,
// 去掉 Override不报错,但是也不是重写
public static void method() {
System.out.println("son");
}
}
public class staticTest {
public static void main(String[] args) {
// 直接类调用
Father.method(); // Father method
Son.method(); // 子类的static方法 son
// 执行 Father 类中的 method
Father f = new Son();
f.method(); // Father method
}
}
单例模式
对某个类只能存在一个实例对象,而且只提供一个取得该类对象实例的方法
实现思路:
- 将类的构造器访问权限设为
private
,外部就不能 new 产生对象了 - 设一个静态方法,返回类的内部创建的实例
- 成员变量和内部创建实例的变量都设为静态
单例模式的两种实现方式
饿汉式
class Singleton {
// 私有化构造器
private Singleton(){}
// 内部创建实例,设为static
private static Singleton singLeton = new Singleton();
// 通过公共的静态方法 返回当前类的对象
public static Singleton getInstance(){
return singleton;
}
}
特点:
- 立即加载,使用类时,对象已经创建完成
- 没有多线程安全问题,实现起来简单
- 耗费内存,类加载时,静态变量被创建并且分配内存空间,此后一直占用这块内存空间,直到类销毁 / 卸载
懒汉式
class Singleton{
// 私有化构造器
private Singleton(){}
// 内部提供实例,设为static
private static Singleton singLeton;
public static Singleton getInstance(){
if(singLeton==null) {
singLeton = new Singleton;
}
return singLeton;
}
}
特点:
- 延迟加载,调用类的静态方法时对象才创建
- 节省内存,在调用方法才创建且分配空间,一定程度上节省了内存空间
- 线程不安全,不能保证单例的唯一性,在多线程环境下这种实现方法是错误的
应用场景
- Windows 的 Task Manager (任务管理器)就是很典型的单例模式
- Windows 的 Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收 站一直维护着仅有的一个实例
- •Application 也是单例的典型应用
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直 处于打开状态,因为只能有一个实例去操作,否则内容不好追加
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源
理解 main 方法的语法
public static void main(String[] args){}
- JVM 要调用 main 方法 ,权限修饰符为 public
- 执行 main 方法时不需要创建对象所以用 static 修饰
- 接收一个 String 类型的数组参数,数组中保存执行 Java 命令时传递给类的参数
例如
public class mainTest {
public static void main(String[] args){
for(int i = 0; i< args.length; i++){
System.out.println("args[" + i + "] = " + args[i]);
}
}
}
命令行执行
java mainTest "fantasy" "jay" "yxl"
输出结果
args[0] = fantasy args[1] = jay args[2] = yxl
代码块
代码块的作用:对 java 类或对象初始化
一个类中代码块若有修饰符,则只能被 static 修饰,称为静态代码块(static block),没有修饰的是非静态代码块
静态代码块
public class Cat {
private String name;
private static int leg;
static {
leg = 4;
System.out.printIn("静态代码块");
}
}
总结:
- 可以有输出语句
- 可以对类的属性、声明进行初始化操作
- 不可以调用非静态方法和属性
- 如果有多个静态代码块,按从上往下顺序执行
- 执行顺序优先于非静态代码块
- 随着类的加载而加载,只执行一次
非静态代码块
意义:如果有多个构造器重载,而且里面有公共代码,即可提取到 非静态代码块里,减少冗余代码,因为优先于构造执行的
public class User {
private String userName;
private String passWord;
private long registrationTime;
{
// 创建 User 对象都会执行代码块里的代码
System.out.printIn("新用户注册");
registrationTime = System.currentTimeMillis();
}
public User(){
userName = registrationTime+"";
passWord = "123456";
}
public User(String userName, String passWord){
this.userName = userName;
this.passWord = passWord;
}
}
特点:
- 可以有输出语句
- 可以对类的属性、类的声明进行初始化操作
- 除了调用非静态的结构外,还可以调用静态的变量或方法
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行
- 每次创建对象的时候,都会执行一次。且先于构造器执行
final 关键字
final 表示最终的不可更改的
final 修饰类
表示类不可继承,没有子类,提高安全性,程序的可读性
例如:String 类、System 类、StringBuffer 类
final 修饰变量和方法
方法:表示方法不可以被子类重写,例如,Object 类中的 getClass()
变量:表示赋值后不可更改,即常量,变量名通常大写字母表示,例如,final double MY_PI = 3.14
注意:如果某个成员变量用 final 修饰后,没有 set 方法,并且必须初始化(可以显式 赋值、或在初始化块赋值、实例变量还可以在构造器中赋值)
abstract 关键字
类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
抽象方法
在父类声明的方法时没有方法体,只有方法签名的方法就是抽象方法
包含抽象方法的类必须是抽象类
使用 abstract
修饰的类 / 方法 就是抽象类 / 方法
例如
public abstract class Person{
public abstract void eat();
}
抽象方法的实现:子类继承抽象类重写类中的抽象方法
public class Student extends Person {
public void eat(){
System.out.printIn("吃饱喝好!!!");
}
}
总结:
抽象类是不能创建对象的,是用来被子类继承的
抽象类的子类必须重写全部抽象方法,并且提供方法体
抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的
抽象类中不一定有抽象方法,但有抽象方法必须是抽象类
interface 接口
接口就是规范,定义的是一组规则,体现了现实世界中 “如果你是/要…则必须 能…” 的思想。继承是一个 “是不是” 的 is-a 关系,而接口实现则是 “能不能” 的 has-a 关系
接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大 家都要遵守
定义格式
关键字 interface
,与定义类的方式相同,它并不是类,而是另外一种引用数据类型
public interface Student {
// 静态常量 通常采用大写
// public static final 可以省略
long CURRENT_TASK = "学习";
// 抽象方法
// public abstract 可以省略
void wirte();
void read();
// 默认方法 JDK8
default void attendClass() {
System.out.printIn("学生需要上课");
}
// 静态方法 JDK8
static void eat() {
System.out.printIn("学生需要吃饱喝好");
}
}
实现接口
接口不能创建对象,但是可以被类实现 关键字 implements
类与接口的关系为实现关系,即类实现接口,该类就被称为接口的实现类
public class xm implements Student {
// ...重写接口的抽象方法
}
// 继承 + 实现
public class xh extends Person implements Student {
// ...
}
注意:
- 实现接口的类非抽象类,必须重写接口中的所有抽象方法
- 接口的默认方法,实现类可以选择重写(重写时不用保留 default)
- 接口的静态方法不能重写也不能被继承
接口的多实现
一个类只能继承一个父类,而对于接口而言,一个 类是可以实现多个接口的,这叫做接口的多实现
【修饰符】 class 实现类 implements 接口 1,接口 2,接口 3... {}
【修饰符】 class 实现类 extends 父类 implements 接口 1,接口 2...{}
注:如果抽象方法有重名的只需要重写一次
接口的多继承
一个接口可以继承另一个或者多个接口,接口继承关键字也是 extends
// 父类
public interface Person {
void sleep();
void eat();
}
public interface Motion {
void run();
void basketball();
}
// 子接口
public interface Student extends Person,Motion {
void read();
}
// 实现类
public class xm implements Student {
void sleep(){
System.out.println("睡觉");
}
void eat(){
System.out.println("吃东西");
}
void run(){
System.out.println("跑步");
}
void basketball(){
System.out.println("篮球");
}
void read(){
System.out.println("读书");
}
}
所有父接口的抽象方法都有重写, 方法签名相同的抽象方法只需要实现一次
接口与实现类对象构成多态引用
接口类型的变量与实现类的对象之间,也可以构成多态引用
通过接口类型的变量调用方法,最终执行的是 你 new 的实现类对象实现的方法体
public interface Motion {
void run();
}
public class Cat implements Motion {
public void(){
System.out.println("猫会跑");
}
}
public class Dog implements Motion {
public void(){
System.out.println("狗会跑");
}
}
// 测试
public class TestInterface{
public static void main(String[] args){
Motion c = new Cat();
c.run();
Motion d = new Dog();
d.run;
}
}
接口的非静态方法
对于接口的静态方法,直接使用“接口名.”进行调用即可
- 也只能使用“接口名.”进行调用,不能通过实现类的对象进行调用
对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
- 接口不能直接创建对象,只能创建实现类的对象
总结
接口本身不能创建对象,只能创建接口的实现类对象,接口类型的变量可以与实现类 对象构成多态引用
声明接口用 interface,接口的成员声明有限制:
公共的静态常量
公共的抽象方法
公共的默认方法(JDK8.0 及以上)
公共的静态方法(JDK8.0 及以上)
私有方法(JDK9.0 及以上)
类可以实现接口,关键字是 implements,而且支持多实现
如果实现类不是抽象类, 就必须实现接口中所有的抽象方法
如果实现类既要继承父类又要实现父接口,那么 继承(extends)在前,实 现(implements)在后
接口可以继承接口,关键字是 extends,而且支持多继承
接口的默认方法可以选择重写或不重写。如果有冲突问题,另行处理。子类重写父接 口的默认方法,要去掉 default,子接口重写父接口的默认方法,不要去掉 default
接口的静态方法不能被继承,也不能被重写。接口的静态方法只能通过 “接口名.静态 方法名” 进行调用
接口和抽象类的对比
内部类
将一个 类A 定义在 另一个类B 里面,类A就被称为内部类,类B为外部类
内部类的分类:
- 成员内部类
- 静态成员内部类
- 非静态成员内部类
- 局部内部类
- 匿名局部内部类
- 非匿名局部内部类
成员内部类
举例
public class Outer {
private static String a ="静态外部类的静态 a";
private String b = "非静态外部类 b"
// 创建静态内部类实例
static class StaticInner {
private static String a ="静态内部类的静态 a";
public static void show(){
System.out.printIn(this.a);
System.out.printIn(Outer.a);
// 不能访问外部类的非静态成员
// System.out.printIn(Outer.b);
}
}
// 创建非静态内部类实例
class NOStaticInner {
private String a ="非静态内部类的非静态 a";
System.out.printIn(this.a);
System.out.printIn(Outer.a);
System.out.printIn(Outer.b);
}
}
// 实例化
public class TestInnerClass {
public static void main(String[] args) {
//创建静态内部类实例
Outer.StaticInner innter = new Outer.StaticInner();
//调用静态内部类静态方法
Outer.StaticInner.show();
// 创建非静态内部类实例
Outer outer = new Outer();
Outer.NoStaticInner inner1 = outer.new NoStaticInner()
}
}
总结:
- 内部类作为类的成员,可以声明为 private 或 protected,调用外部类的结构,声明为 static 的,但此时就不能使用外层类的非 static 的 成员变量
- 内部类作为类的角色,可以定义属性、方法、构造器等结构,继承父类,实现接口,声明为 abstract 抽象类,声明 final
局部内部类
举例:
public class Outer {
public static void outerMethod(){
class Inner{
public void innerMethod(){
System.out.printIn("局部内部类方法")
}
}
Inner inner = new Inner();
inner.innerMethod();
}
}
- 和成员内部类不同的是,它前面不能有权限修饰符等
- 和局部变量一样,有作用域
- 编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、 编号
匿名内部类
举例:使用匿名内部类对象直接调用方法
interface A {
public void show();
}
public class TestInnerClass(){
public static void main(String[] args) {
new hello(){
@Override
public void show(){
System.out.printIn("hello world");
}
}.show();
}
}
举例:通过父类或接口的变量多态引用匿名内部的对象
interface A {
public void show();
}
public class TestInnerClass(){
public static void main(String[] args) {
A a = new A(){
@Override
public void show(){
System.out.printIn("hello world");
}
}
a.show();
}
}
举例:匿名内部类的对象作为实参
interface A {
public void show();
}
public class TestInnerClass(){
public static testMethod(A a){
a.show();
}
public static void main(String[] args) {
testMethod(new A{
@Override
public void show(){
System.out.printIn("hello world");
}
})
}
}
枚举类
枚举类本质上也一个类,只不过类的对象有限,固定好,不能让用户随意更改
enum
关键字声明枚举 ,JDK5.0 之后
举例:方向,上下左右
public enum Direction {
LETF,RIGHT,TOP,BOTTOM;
}
public class TestEnum {
public static void main(String[] args) {
Direction top = Direction.TOP;
}
}
举例:季节,春夏秋冬
public enum Season{
SPRING("春天","春风又绿江南岸"),
SUMMER("夏天","映日荷花别样红"),
AUTUMN("秋天","秋水共长天一色"),
WINTER("冬天","窗含西岭千秋雪");
private final String seasonName;
private final String seasonDesc;
private Season(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
总结:
- 枚举类的常量对象列表必须在枚举类的首行,常量规范大写
- 实例系统会自动添加 public static final 修饰
- 常量后面没有代码可以省略
;
,否则必须添加上 - 默认提供的是 private 的无参构造
- 需要有参构造,手动添加,private 可以省略,调用时,常量对象名(实参列表)
- 默认继承的是 java.lang.Enum 类,因此不能再继承其他的类型
- JDK5.0 之后 switch,提供支持枚举类型,case 后面可以写枚举常量名,无需添加枚 举类作为限定
常用方法
String toString()
:默认返回的是常量名(对象名),可以继续手动重写该方法static 枚举类型[] values()
:返回枚举类型对象的数值,为静态方法static 枚举类型 valueOf(String name)
:把一个字符串转为枚举类对应的对象,如果字符串不是,会有运行时异常:IllegalArg umentExceptionString name()
:获取枚举常量的名字int ordinal()
:返回当前枚举常量的次序号,从 0 开始
实现接口
- 如果每一个枚举值都需要执行同一个方法行为,统一实现接口的方法即可
- 如果每一个枚举值都需要执行同一个方法不同行为,则让每个枚举值分别来实现该方法
interface Info{
void show();
}
// 统一实现,直接重写方法即可
enum Direction1 implements Info {
LETF,RIGHT,TOP,BOTTOM;
@Override
public void show() {
System.out.printIn("Direction");
}
}
// 枚举类的每一个对象重写接口中的抽象方法
// 执行的是不同的实现的方法
public enum Direction implements Info{
LEFT{
@Override
public void show() {
System.out.println("左边");
}
},RIGHT {
@Override
public void show() {
System.out.println("右边");
}
},TOP {
@Override
public void show() {
System.out.println("上边");
}
},BOTTOM {
@Override
public void show() {
System.out.println("下边");
}
};
}
注解
注解(Annotation)是从 JDK5.0 开始引入,以
@注解名
在代码中存在
注解可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。可添加一些参数值,这些信息被保存在 Annotation 的 name=value
对中
注解可以在类编译、运行时进行加载,体现不同的功能
常用注解
生成文档相关的注解:
@author
标明开发该类模块的作者,多个作者之间使用,分割@version
标明该类模块的版本@see
参考转向,也就是相关主题@since
从哪个版本开始增加的@param
对方法中某参数的说明,如果没有参数就不能写@return
对方法返回值的说明,如果方法的返回值类型是 void 就不能写@exception
对方法可能抛出的异常进行说明 ,如果方法没有用 throws 显式抛出的 异常就不能写
在编译时进行格式检查(JDK 内置的三个基本注解):
@Override
: 限定重写父类方法,该注解只能用于方法@Deprecated
: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择@SuppressWarnings
: 抑制编译器警告
@Override
- 用于检测被标记的方法为有效的重写方法,如果不是,则报编译错误
- 只能标记在方法上
- 它会被编译器程序读取
@Deprecated
- 用于表示被标记的数据已经过时,不推荐使用
- 可以用于修饰 属性、方法、构造、类、包、局部变量、参数
- 它会被编译器程序读取
@SuppressWarnings
- 抑制编译警告。当我们不希望看到警告信息的时候,可以使用 SuppressWarnings 注 解来抑制警告信息
- 可以用于修饰类、属性、方法、构造、局部变量、参数
- 它会被编译器程序读取
元注解
元注解就是注解到其他注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其他的注解上面
JDK1.5 在 java.lang.annotation 包定义了 4 个标准的 meta-annotation 类型,它 们被用来提供对其它 annotation 类型作说明
@Target
用于描述注解的使用范围,通过枚举类型 ElementType 的 10 个常量对象来指定范围
@Retention
用于描述注解的生命周期,通过枚举类型 RetentionPolicy 的 3 个常量对象来指定
SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
@Documented
表明这个注解应该被 javadoc 工具记录
@Inherited
允许子类继承父类中的注解
自定义注解
一个完整的注解应该包含三个部分: (1)声明 (2)使用 (3)读取
【元注解】
【修饰符】 @interface 注解名{
【成员列表】
}
举例
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface myAnnotation {
String value();
}
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String columnName();
String columnType();
}
- 自定义注解可以通过四个元注解@Retention,@Target,@Inherited,@Documented,分别说明它的声明周期,使用位置,是否被继承,是否被生成到 API 文档中。
- Annotation 的成员在 Annotation 定义中以无参数有返回值的抽象方法的形式来声
明,我们又称为配置参数。返回值类型只能是八种基本数据类型、String 类型、Class
类型、enum 类型、Annotation 类型、以上所有类型的数组 - 可以使用 default 关键字为抽象方法指定默认返回值
- 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值。格式
是方法名 = 返回值
,如果只有一个抽象方法需要赋值,且方法名为 value,可以省
略value=
,所以如果注解只有一个抽象方法成员,建议使用方法名 value
JUnit 单元测试
JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个测试框架(regression testing framework),供 Java 开发人员编写单元测试之用
导入 JUnit 单元测试后 ,测试代码无需创建 main 方法 ,声明静态方法…,在方法添加 @Test
即可运行此方法
import org.junit.Test;
public class TestJUnit {
@Test
public void test01(){
System.out.println("TestJUnit.test01");
}
@Test
public void test02(){
System.out.println("TestJUnit.test02");
}
}
JUnit4 版本,要求@Test 标记的方法必须满足如下要求:
- 所在的类必须是 public 的,非抽象的,包含唯一的无参构造器
- @Test 标记的方法本身必须是 public,非抽象的,非静态的,void 无返回值,()无参 数的。
包装类
Java 针对八种基本数据类型定义了相应的引用类型:包装类(封装类)
基本数据类型 | 包装类 |
---|---|
byte | Byet |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
除了 int char 其他基本数据类型的包装类都是首字母大写
基本数据类型和包装类转化
基本数据类型转包装类(装箱)
// 构造函数
Integer num1 = new Integer(10);
Double num2 = new Double(10.2);
// 包装类的 valueOf 静态方法
Integer num3 = Integer.valueOf(36);
包装类转基本数据类型(拆箱)
Integer num3 = Integer.valueOf(36);
int num4 = num3.intValue();
Double f2 = Double.valueOf(10.2);
float f3 = f2.floatValue();
自动转化(自动装箱与拆箱)
从 JDK5.0 开始,基本类型与 包装类的装箱、拆箱动作可以自动完成
// 自动装箱
// 相当于 Integer i = Integer.valueOf(4);
Integer i = 10;
// 等号右边:将i对象转成基本数值(自动拆箱)
// 相当于 i.intValue() + 5
// 运算完成后再次装箱,把基本数值转成对象
i = i + 5;
基本数据类型、包装类与字符串间的转换
基本数据类型转为字符串:
int a = 10;
// 方式 1:调用字符串重载的 valueOf()方法
String str = String.valueOf(a);
// 方式 2:运输符拼接字符串
String str = a + "";
字符串转为基本数据类型:
方式 1:除了 Character 类之外,其他所有包装类都具有 parseXxx 静态方法可 以将字符串参数转换为对应的基本类型
String str = "10";
int num = Integer.parseInt(str);
方式 2:字符串转为包装类,然后可以自动拆箱为基本数据类型
String str = "10";
int num = Integer.valueOf(str);
方式 3:通过包装类的构造器实现
int a = new Integer("123");
double d = new Double("123.55");
参考资料