一、代码块
什么是代码块:
在类或者在方法中,直接使用"{}"括起来的一段代码,表示一块代码区域。
代码块里变量属于局部变量,只在自己所在区域(前后的{})内有效。
根据代码块定义的位置的不同,我们又分成三种形式:
1):局部代码块:直接定义在方法内部的代码块:
2):初始化代码块(构造代码块):直接定义在类中.
3):静态代码块:使用static修饰的初始化代码块.
使用static修饰的初始化代码块,就叫静态代码块。一个成员,不加static属于对象,加static属于类。在主方法执行之前,执行静态代码块,只执行一次。main()方法是程序入口,为何静态代码块优先于main方法。静态成员随着字节码加载而加载进入JVM,此时main方法未执行,因为方法需要调用。先把字节码加载进入JVM,而后JVM调用main方法。一般的,我们用来做初始化操作,加载资源,加载配置文件。
具体使用情况如下:
class CodeBlockDemo
{
{
//--------初始化代码块------//
System.out.println("初始化代码块");//编译后,此句进入构造。
//--------------------------//
}
CodeBlockDemo()
{
System.out.println("构造器");
}
static
{
//--------静态代码块------//
System.out.println("静态代码块");
//--------------------------//
}
public static void main(String[] args)
{
{//局部代码块
int age = 17;
System.out.println(age);
}
System.out.println("进入main()方法");
//创建三个CodeBlockDemo对象
new CodeBlockDemo();
new CodeBlockDemo();
new CodeBlockDemo();
}
}
/*
非static字段的初始化是在构造器中优先执行的
static成员通过静态代码块做初始化的
*/
但是通过反编译之后,我们可以看到:
二、final修饰符
关于final修饰符:
多个修饰符之间是没有先后关系的,如:public static / static public
final表示可以修饰类,方法,变量(均为非抽象)
(构造器不能使用final修饰)
①final修饰的类: 表示最终类,不能再有子类了。
一般的,这些类设置为final类:
1.本类不是为继承而设计的。
2.除以安全考虑,不允许子类改动,不准修改源代码。
3.确定该类不会再拓展。
② final修饰的方法:最终方法,该方法不能被子类覆盖。
一般的,这些方法设置为final方法:
1.在父类中,提供统一的算法骨架,就不准子类通过方法覆盖
2.在构造器中,调用的方法(初始化方法)。初始化方法一般使用final修饰
final修饰的变量:表示常量,只能赋值一次。不能再次赋值
final是唯一可以修饰局部变量的修饰符,必须做一次初始化。
常量的命名规范:
单词全部使用大写字母,若多个单词组成,使用下划线隔开
全局静态常量:public static final 修饰的变量,直接使用类名调用即可
final修饰的引用类型变量到底是表示引用的地址不能被改变?或是引用空间的数据不能改变
答:
class Person
{
public String info = "初值";
}
class FinalDemo
{
public static void main(String[] args)
{
final Person p = new Person();
System.out.println(p.info);
p.info = "xxx";
System.out.println(p.info);
}
}
//如上:不能重新分配内存地址,但是空间内容是可以改变。
//一般的,此时我们使用final修饰变量
//当在程序中,多个共同的地方使用数据,且此数据不会改变
三、单例设计模式
保证在整个项目中,某一个类有且仅有一个实例(一个类在内存中只有一个对象)
即所有指向该类型的引用都指向同一块内存
如需求:定义一个数组的工具类,使用单例模式解决
单例的编写有N种写法,单讲一种:饿汉式
必须在该类中先自己创建一个对象
私有化自身的构造器,防止外界通过构造器创建新对象
向外暴露一个公共静态方法用于获取自身的对象
class ArrayUtil
{
//必须在该类中,自己先创造一个对象
private static final ArrayUtil instance = new ArrayUtil();
//私有化自身的构造器,防止外界通过构造器创建写的对象
private ArrayUtil(){}
//向外暴露一个公共静态方法用于获取自身的对象
public void sort(int[] arr)
{
System.out.println("排序");
}
public static ArrayUtil getInstance()
{
return instance;
}
}
class SingletonDemo
{
public static void main(String[] args)
{
//ArrayUtil arr = new ArrayUtil();//此句话将报错 错误: ArrayUtil()可以在ArrayUtil中访问private
System.out.println(ArrayUtil.getInstance() == ArrayUtil.getInstance());
ArrayUtil.getInstance().sort(null);
}
}
四、基本类型包装类
问题1:在面向对象中,“一切皆对象”,现在问题来了。int age = 17 此代码哪有对象?
矛盾点:基本类型变量不是对象此时有矛盾。问题2:现在给你一个复杂的十进制数据,以最快的速度在程序中转换成二进制,八进制,十六进制
矛盾点:算法太麻烦了问题3:现在使用double表示学生考试成绩,但是你怎么表示一个人缺考和考试得零分。
矛盾点:double初值为0.0
上述的问题,进就是因为基本数据类型缺少对象,如果需要对象,必须先有类.
此时我们可以为每一个基本类型都编写一个对应的包装类,类中包含了该基本类型的一个值.比如针对第二个问题,我们把int类型进行包装:
//包装int类型的值
public class IntWapper
{
private int value;//值
public IntWapper(int value)
{
this.value = value;
}
//包含int类型相关的算法,把十进制转换2进制/8进制/16进制
}
....main......
{
IntWapper wapper = null; //没有对象,没有数据
IntWapper wapper = new IntWapper(0);
}
那么,我们将八大基本数据类型一一包装之后,就可以得到:
(以上几个类,都是由final修饰过的,因此不能存在子类对其进行继承)
装箱:把基本类型数据转成对应的包装类对象。
拆箱:把包装类对象转成对应的基本数据类型数据。
Sun公司从Java5开始提供了的自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)功能 :
自动装箱:可把一个基本类型变量直接赋给对应的包装类变量。
自动拆箱:允许把包装类对象直接赋给对应的基本数据类型变量。
class Demo
{
public static void main(String[] args)
{
//手动装箱
Integer i1 = new Integer(17);
Integer i2 = Integer.valueOf(17);
//推荐i2 因为该方法有可能通过缓存经常请求的值而显著提高空间和时间性能
System.out.println(i1.equals(i2));
System.out.println(i1 == i2);
//手动拆箱
int i3 = i2.intValue();
//自动装箱:
Integer a1 = 17;//编译:Integer a1 = Integer.valueOf(17)
//自动拆箱
int a2 = a1; //编译:int a2 = a1.intValuel
}
}
包装类中的缓存设计(享元模式),本质就是缓存设计:
Byte、Short、Integer、Long:缓存[-128,127]区间的数据;
Character:缓存[0,127]区间的数据;
//包装类的缓存Cache
class CacheDemo
{
public static void main(String[] args)
{
Integer i1 = new Integer(123);
Integer i2 = new Integer(123);
System.out.println(i1 == i2); //false 每次new都开辟新空间,==比较内存地址
Integer i3 = Integer.valueOf(123);
Integer i4 = Integer.valueOf(123);
System.out.println(i3 == i4); //true 在(-128,127)范围,就获取缓存中的数据,并没有new
Integer i5 = 123;
Integer i6 = 123;
System.out.println(i5 == i6); //true 自动装箱底层也为手动装箱,也就是i3,i4那种方式
System.out.println("---------------------------------------");
Integer i11 = new Integer(321);
Integer i22 = new Integer(321);
System.out.println(i11 == i22); //false
Integer i33 = Integer.valueOf(321);
Integer i44 = Integer.valueOf(321);
System.out.println(i33 == i44); //false 321不在(-128,127),就new一个Integer
Integer i55 = 321;
Integer i66 = 321;
System.out.println(i55 == i66); //false
System.out.println(i55.equals(i66)); //true
//其底层把包装类类型拆箱为基本类型,再比较其值
//包装类比较数据用equals,即使是数值在-128和127之间。
}
}
1.默认值:int默认值为0,Integer默认值为null
Integer既可以表示null,也可以表示0。
2.包装类提供了该类型相关的许多算法与操作方法。
如:static String toBrinaryString(int i);
static String toOctalString(int i);
static String toHexString(int i);
3.在集合框架中,只能存储对象类型,不能存储基本数据类型值
4.int和Integer是同一种类型吗?
不是。但同名方法可以共存。
5.方法中的基本类型变量主要存储在栈中,包装类存放于堆中。
开发中建议使用包装类型。
五、抽象方法和抽象类
引出抽象方法:
假如一个图形都有“求面积”这个概念,那么无论是圆,正方形,三角形,我们都对其要求面积。
我们把它抽象为Graph类,我们可以把矩形,圆形继承Graph类。
但是我们传入的参数不一样,比如圆形的R,矩形的width和height。
子类求面积的方法也是不同的。
所以子类必须覆盖求面积的方法。
class Graph
{
return 0.0;
}
class Circle extends Graph
{
...CODE...
public double getArea(){}
}
class Triangle extends Graph
{
...CODE...
public double getArea(){}
}
此时,我们可以试着使用abstract关键字对其进行修饰
使用abstract修饰且没有方法体的方法,称为抽象方法。
特点:
① 使用抽象abstract修饰,方法没有方法体,留给子类去实现/覆盖。
② 抽象方法修饰符不能是private 和 final以及static。
③ 抽象方法必须定义在抽象类或接口中。
一般的:习惯性把abstract写在方法修饰符最前面,一看就知道是抽象方法。
使用abstract关键字修饰的类。
特点:
① 不能创建实例即不能new一个抽象类,即使创建出抽象类对象,调用抽象方法,根本没有方法体。
② 可以不包含抽象方法,若一旦包含,该类必须作为抽象类,抽象类可以包含普通方法(留给子类调用的),抽象类是有构造器的,子类构造器必须先调用父类构造器。
③ 若子类没有实现/覆盖父类所有的抽象方法,那么子类也得作为抽象类(抽象派生类)。
④ 构造方法不能都定义成私有的,否则不能有子类(创建子类对象前先调用父类构造方法)。
⑤ 抽象类不能使用final修饰,因为必须有子类,抽象方法才能得以实现。
⑥ 是不完整的类,需作为父类(必须要有子类),功能才能得以实现。
抽象类:一般的,我们起名,习惯使用Abstract作为前缀,让调用者一看就知道是抽象类.
抽象类中可以不存在抽象方法,如此这样没有太大的意义,但是可以防止外界创建对象,所以我们会发现有些工具类没有抽象方法,但是也使用abstract来修饰…
抽象类不能实例化:
抽象类和普通类的区别:
普通类有的成员(方法,字段,构造器),抽象类都有。
抽象类不能创建对象,抽象类中可以包含抽象方法。