记录学习----------JavaSE模块二
文章目录
JavaSE学习小结一、封装的概念与示例二、构造快 与 静态代码块
1.构造块:在类体中直接使用{}括起来的代码块。2.静态代码块:使用static关键字修饰的构造块。3.构造块 与 静态代码块的笔试考点 三、单例设计模式
饿汉式创建单例对象懒汉式创建单例对象进一步优化: 针对线程安全的优化(目前自身能力只能考虑到这里了) 四、final关键字五、多态
1. 多态的概念2.多态的特点3. 类型转换
3.1自动类型转换 (向上转型)3.2强制类型转换 (向下转型)3.3类型转换要注意的问题 4.多态的实际意义 六、抽象方法 与 抽象类 (与多态结合)七、接口 --- 弥补Java语言不支持多继承的不足八、内部类九、枚举类型十、注解总结
一、封装的概念与示例
通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无
论是编译阶段还是运行阶段都不会报错或者给出提示,此时与现实生活
不符。为了避免上述错误的发生,就需要对成员变量进行密封包装处理,来隐
藏成员变量的细节以及保证成员变量数值的合理性,该机制就叫做封装
代码如下(示例):
public class People {
private String name;
private int age;
private boolean gender; // true 等价于 men, false 等价于 women
public People() {}
public People(String name, int age, boolean gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 0 && age < 150)
this.age = age;
else
System.out.println("error!");
}
//这个是boolean类型的getter
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
@Override
public String toString() {
String gd = "wemon";
if(isGender())
gd = "men";
return "People{" +
"name='" + name + ''' +
", age=" + age +
", gender=" + gd +
'}';
}
}
二、构造快 与 静态代码块 1.构造块:在类体中直接使用{}括起来的代码块。
每创建一个对象都会执行一次构造块 2.静态代码块:使用static关键字修饰的构造块。
静态代码块随着类加载时执行一次
代码如下(示例):
public class BlockTest {
{
System.out.println("构造块");
}
static {
System.out.println("******静态代码块******");
}
BlockTest() {
System.out.println("构造方法体");
}
public static void main(String[] args) {
BlockTest t1 = new BlockTest();
BlockTest t2 = new BlockTest();
}
}
运行结果如下(示例):
public class SuperTest {
{
System.out.println("SuperTest类中的构造块!"); // a
}
static {
System.out.println("SuperTest类中的静态代码块!"); // b
}
public SuperTest() {
System.out.println("SuperTest类中的构造方法体!"); // c
}
public static void main(String[] args) {
// 使用无参方式构造对象
SuperTest st = new SuperTest();
}
}
public class SubSuperTest extends SuperTest {
{
System.out.println("==========SubSuperTest类中的构造块!"); //d
}
static {
System.out.println("==========SubSuperTest类中的静态代码块!"); //e
}
public SubSuperTest() {
//System.out.println("==========SubSuperTest类中的构造方法体!"); // f
out.println("==========SubSuperTest类中的构造方法体!");
}
public static void main(String[] args) {
// 使用无参方式构造子类的对象
SubSuperTest sst = new SubSuperTest();
}
}
运行结果为: b e a c d f
这是因为子类要先创建父类,并且静态代码块是随着类的加载而运行的,因此两个静态代码块会先输出内容,之后才是父类的构造快、父类的构造方法、子类的构造块、子类的构造方法。
三、单例设计模式
单例设计模式的概念:在某些特殊场合中,一个类对外提供且只提供一个对象时,这样的类叫
做单例类,而设计单例的流程和思想叫做单例设计模式。单例设计模式的实现流程:
- 私有化构造方法,使用private关键字修饰。声明本类类型的引用指向本类类型的对象,并使用private static关键字共
同修饰。提供公有的get方法负责将对象返回出去,并使用public static关键字共同
修饰。
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
代码示例:
public class Singleton {
// 2. 声明本类类型的引用指向本类类型的对象,并使用private static关键字共同修饰
private static Singleton ob1 = new Singleton();
// 1.私有化构造方法
private Singleton() {}
// 3.提供公有的get方法负责将对象返回出去,并使用public static关键字共同修饰
public static Singleton getOb1() {
return ob1;
}
}
懒汉式创建单例对象
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。
代码示例:
public class Singleton {
// 2. 声明本类类型的引用指向本类类型的对象,并使用private static关键字共同修饰
private static Singleton ob1 = null;
// 1.私有化构造方法
private Singleton() {}
// 3.提供公有的get方法负责将对象返回出去,并使用public static关键字共同修饰
public static Singleton getOb1() {
if(ob1 == null)
ob1 = new Singleton();
return ob1;
}
}
目前上面这段代码已经实现了一个懒汉式的单例模式,虽然不完美但是不影响我们使用它。
进一步优化: 针对线程安全的优化(目前自身能力只能考虑到这里了)我们先看一下懒汉式单例模式的核心方法:
public static Singleton getOb1() {
if(ob1 == null)
ob1 = new Singleton();
return ob1;
}
这个方法之所以存在线程安全的问题是因为,如果存在两个线程 A 和 B,同时在判断得到 Singleton 对象 ob1 为 null ,就会调用构造方法,对 ob1 进行实例化,这样的话我们得到的就不是单例而是 “ 双 ” 例了。所以我们要做的就是解决线程安全问题。
其中最简单的解决方法就是 :加锁,那么就可以得到一下代码:
public static synchronized Singleton getOb1() {
if (ob1 == null) {
ob1 = new Singleton();
}
return ob1;
}
// 或者
public static Singleton getOb1() {
synchronized(Singleton.class) {
if (ob1 == null) {
ob1 = new Singleton();
}
}
return singleton;
}
接下来我们要做的就是优化性能,这是因为每次去获取对象都需要先获取锁,并发性能非常地差。因此我们的目标就是:如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例
public static Singleton getOb1() {
if (ob1 == null) { // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
if (ob1 == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
ob1 = new Singleton();
}
}
}
return ob1;
}
第2行代码,如果singleton不为空,则直接返回对象,不需要获取锁;而如果多个线程发现singleton为空,则进入分支;第3行代码,多个线程尝试争抢同一个锁,只有一个线程争抢成功,第一个获取到锁的线程会再次判断singleton是否为空,因为singleton有可能已经被之前的线程实例化其它之后获取到锁的线程在执行到第4行校验代码,发现singleton已经不为空了,则不会再new一个对象,直接返回对象即可之后所有进入该方法的线程都不会去获取锁,在第一次判断singleton对象时已经不为空了
四、final关键字
final关键字修饰类体现在该类不能被继承。
主要用于防止滥用继承,如:java.lang.String类等,示例如下:
其中我们可以看到IDEA给我们的报错很明确:Cannot inherit from final 即不能继承被final关键字修饰的类
final关键字修饰成员方法体现在该方法不能被重写但可以被继承。
主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等。示例如下:
public final void printMas() {
System.out.println("This is a method modified by final.");
}
public void printMas() {
System.out.println("You can override it.");
}
final关键字修饰成员变量体现在该变量必须初始化且不能改变。
主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等。
其实用final关键字修饰变量不太常用,我们更多的是用public static final进行修饰表示一个常量,比如:自然对数的底e≈ 2.71828
public static final double E = 2.71828;
五、多态 1. 多态的概念
Java的三大特性分别是:封装、继承和多态
同一事务表现出的多种形态,语法格式: 父类类型 引用变量名 = new 子类类型();示例如下:
public class Shape {
private int x;
private int y;
public Shape() {
}
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void printMas() {
System.out.println("横坐标是:" + getX() + " 纵坐标是:" + getY());
}
@Override
public String toString() {
return "Shape{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class Rect extends Shape{
private int len;
private int high;
public Rect() {
}
public Rect(int x, int y, int len, int high) {
super(x, y);
setLen(len);
setHigh(high);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0)
this.len = len;
else
System.out.println("长度不能小于零");
}
public int getHigh() {
return high;
}
public void setHigh(int high) {
if(high > 0)
this.high = high;
else
System.out.println("高度不能小于零");
}
@Override
public void printMas() {
System.out.println("横坐标是:" + getX() + " 纵坐标是:" + getY() + " 长度是:" + getLen()
+ " 高度是:" + getHigh());
}
@Override
public String toString() {
return "Rect{" +
"len=" + len +
", high=" + high +
'}';
}
}
Shape类表示一个图形,Rect类表示的是圆形,二者之间存在着IS-A的关系,接下来对他们进行测试
首先我们采用创建本类实例的方式,先看一下运行结果:
public class ShapeRectTest {
public static void main(String[] args) {
Shape sh = new Shape(10, 26);
Rect re = new Rect(12,34,56,78);
sh.printMas(); // 横坐标是:10 纵坐标是:26
re.printMas(); // 横坐标是:12 纵坐标是:34 长度是:56 高度是:78
}
接下来我们采用多态的方式进行测试:
public class ShapeRectTest {
public static void main(String[] args) {
Shape a = new Rect(127, 348, 578, 758);
//披着羊皮的狼,a具体是什么类型要看后面new的是哪个,因此会调用子类Rect中的printMas()方法
a.printMas();
// 横坐标是:127 纵坐标是:348 长度是:578 高度是:758
}
}
2.多态的特点
当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法。当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法。对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)。对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本 3. 类型转换 3.1自动类型转换 (向上转型)
上转型在前面已经用到了,就是:Shape a = new Rect(127, 348, 578, 758); 由子类转向为父类
3.2强制类型转换 (向下转型)前面提到,父类类型的引用是不能直接调用子类独有方法的,如果就是想调用,就需要使用强转的方式,示例如下:
public class ShapeRectTest {
public static void main(String[] args) {
Shape a = new Rect(127, 348, 578, 758);
// a.getLen(); 这样写会报错,因为父类不能直接调用子类独有方法,需要强转,如下所示
int len = ((Rect) a).getLen();
System.out,println(len);
}
}
在进行类型转换时我们必须要注意一个细节,那就是转换必须发生在父子类之间
3.3类型转换要注意的问题- 若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常
public class ShapeRectTest {
public static void main(String[] args) {
Shape a = new Rect(127, 348, 578, 758);
//若由一个类ShapeB继承自Shape, 那么下面的写法是错误的。虽然编译时不会报错,但是运行时会出错
//这是因为 a 虽然是Shape类型的实例,但是在运行时其本质是Rect类,而Rect类和ShapeB类之间不存在继承关系
ShapeB b = (ShapeB)a;
}
}
- 对此我们的解决方法也很简单,就是使用 instanceof 关键字
public class ShapeRectTest {
public static void main(String[] args) {
Shape a = new Rect(127, 348, 578, 758);
// 判断引用变量指向的对象是否为后面的数据类型
if(a instanceof ShapeB) {
ShapeB b = (ShapeB)a;
a.printMas();
}
else {
System.out.println("error");
}
}
}
4.多态的实际意义
多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果,例如:
public class ShapeTest {
// 自定义成员方法实现既能打印矩形对象又能打印圆形对象的特征,对象由参数传入 子类 is a 父类
// Shape s = new Rect(1, 2, 3, 4); 父类类型的引用指向子类类型的对象,形成了多态
// Shape s = new Circle(5, 6, 7); 多态
// 多态的使用场合一:通过参数传递形成了多态
public static void draw(Shape s) {
// 编译阶段调用父类的版本,运行阶段调用子类重写以后的版本
s.show();
}
public static void main(String[] args) {
// Rect r = new Rect(1, 2, 3, 4);
// r.show();
ShapeTest.draw(new Rect(1, 2, 3, 4));
ShapeTest.draw(new Circle(5, 6, 7));
}
}
六、抽象方法 与 抽象类 (与多态结合)
抽象类中要包含抽象方法,如果没有那么抽象类不能实例化的意义就不存在了
抽象类有构造方法但不能new对象,但可以让子类通过super()的方式调用构造方法
抽象方法 — 一个方法必须存在,但会因为在不同情况下需要有不同的用处是,可以定义为抽象方法,之后再在子类中根据需求重写他
七、接口 — 弥补Java语言不支持多继承的不足
八、内部类
九、枚举类型
十、注解
总结
持续更新中……



