一、用构造器确保初始化
1.构造器的名字和类名完全相同
2.初始化和创建捆绑在一起
3.构造器没有返回值
默认的构造器没有参数,但是我们可以自己创建带参数的构造器。如果创建的带参构造器是唯一构造器,则编译器将不再允许使用默认构造器。
eg:
/**
* 构造器Demo
*/
public class ConstructorDemo
{
public static void main(String[] args)
{
// Student s1 = new Student(); 编译不通过
Student s2 = new Student("s001", 25, "Cosmo");
}
}
class Student
{
String sId;
int age;
String name;
public Student(String sId,int age,String name)
{
System.out.println("sId="+sId+",age="+age+",name="+name);
}
}
复制代码
二、方法重载
其实就是在同一个类中,有两个或多个方法同名。
2-1.类似在日常生活中:
定义一个方法 feed() 用来表示饲养, 则养猫、养狗、养兔子可以通过给feed()传入不同类型的参数来实现:feed(Cat cat),feed(Dog dog),feed(Rabbit rabbit)
2-2.区分重载方法:
每个重载方法都必须有独一无二的参数类型列表
虽然参数顺序不同也可以区分重载方法,但是,这样会影响代码的可读性
无法通过返回值来区分重载方法
2-3.涉及基本数据类型的重载:
public class OverloadingTest
{
@Test
public void test(){
int x = 0;
shortToLarge(x);
largeToShort((short) x);
}
void shortToLarge(double x){
System.out.println("double");
}
void largeToShort(short x){
System.out.println("short");
}
}
复制代码
数据类型从大到小: double>float>long>int>short(char)>byte
当实参的数据类型小于形参的数据类型时,实参的数据类型会被提升。 例如:f1(double d),传入实参为int类型,则实际参数的数据类型会被提升为double类型
char类型是个特例,如果无法找到恰好接受char参数的方法,则会把char类型直接提升为int类型
当实参的数据类型大于形参的数据类型时,实参需要进行强制类型转换,否则编译器会报错。
例如:f1(short s),传入实参为int类型,则需要对int类型的参数进行强制类型转换,将其转换为short类型。
三、this关键字
用途:
1.方法内部获得对当前对象的引用
public class ThisDemoTest1 {
@Test
public void test() throws Exception {
Person person = new Person();
person.setName("KosmoLeung");
Assert.assertEquals("KosmoLeung",person.getName());
}
}
class Person{
private String name;
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
}
复制代码
2.将当前对象传给其他方法
public class ThisDemoTest2 {
@Test
public void test() throws Exception {
User user = new User();
Apple apple = new Apple();
user.eat(apple);
}
}
//苹果类
class Apple{
Apple getPeeled(){
System.out.println("执行削皮操作");
return Peeler.peel(this);
}
}
//削皮器类
class Peeler{
static Apple peel(Apple apple){
//削皮
System.out.println("苹果已削皮");
return apple;
}
}
//人类
class User{
//吃苹果
public void eat(Apple apple){
apple.getPeeled();
}
}
复制代码
3.构造方法中调用构造方法
class Cat
{
private String number;
private String name;
private int age;
Cat(String number)
{
System.out.println("调用了这个构造方法");
this.number = number;
}
Cat(int age)
{
this.age = age;
}
Cat(String number,String name,int age)
{
this(number);
// this(age); 构造方法调用构造方法只能使用一次
this.name = name;
this.age = age;
}
}
复制代码
四、成员初始化
局部变量:编译器强制要求初始化,否则报错
成员变量:未初始化则赋一个默认值
构造器初始化:
eg:
class Dog
{
private int age;
Dog()
{
//构造器初始化
age = 3;
System.out.println(age);
}
}
复制代码
初始化顺序:
public class OrderOfInitializationTest {
public static void main(String[] args) {
House house = new House();
house.method();
}
}
class Window{
public Window(int flag) {
System.out.println("Window--->"+flag);
}
}
class House{
Window w1 = new Window(1); //在House构造方法之前
public House(){
System.out.println("House()");
w3 = new Window(33); //在House构造方法中
}
Window w2 = new Window(2); //在House构造方法之后
void method()
{
System.out.println("method()");
}
Window w3 = new Window(3); //在代码的末尾
}
复制代码
运行结果:
Window--->1
Window--->2
Window--->3
House()
Window--->33
method()
结论:
先初始化变量,后进行构造器初始化
(后面章节还会介绍有继承关系情况下的初始化顺序、存在静态变量的初始化顺序)
静态数据的初始化
public class OrderOfStaticInitializationTest {
public static void main(String[] args) {
System.out.println("创建Room对象");
new Room();
System.out.println("创建Room对象");
new Room();
table.f2(1);
room.f3(1);
}
static Table table = new Table();
static Room room = new Room();
}
class Box{
Box(int flag)
{
System.out.println("Box--->"+flag);
}
void f1(int flag)
{
System.out.println("f1--->"+flag);
}
}
class Table{
static Box box1 = new Box(1);
public Table(){
System.out.println("Table()");
box2.f1(1);
}
void f2(int flag)
{
System.out.println("f2--->"+flag);
}
static Box box2 = new Box(2);
}
class Room{
Box box3 = new Box(3);
static Box box4 = new Box(4);
public Room(){
System.out.println("Room()");
box4.f1(2);
}
void f3(int flag)
{
System.out.println("f3--->"+flag);
}
static Box box5 = new Box(5);
}
复制代码
运行结果:
Box--->1
Box--->2
Table()
f1--->1
Box--->4
Box--->5
Box--->3
Room()
f1--->2
创建Room对象
Box--->3
Room()
f1--->2
创建Room对象
Box--->3
Room()
f1--->2
f2--->1
f3--->1
编译分析:
1.程序首先执行第10行代码:
static Table table = new Table();
复制代码
对静态变量table进行初始化
1-1.对table进行初始化,首先要执行变量初始化操作。
执行第26行代码:
static Box box1 = new Box(1);
复制代码
静态变量box1初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->1
1-2 执行第35行代码:
static Box box2 = new Box(2);
复制代码
静态变量box2初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->2
1-3 执行table构造方法:
public Table(){
System.out.println("Table()");
box2.f1(1);
}
复制代码
屏幕输出Table()
box2执行成员方法f1(),屏幕输出f1--->1
2.程序执行第11行代码:
static Room room = new Room();
复制代码
对静态变量room进行初始化
2-1.对room初始化,首先执行静态变量初始化操作:
执行第41行代码:
static Box box4 = new Box(4);
复制代码
静态变量box4初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->4
执行第50行代码:
static Box box5 = new Box(5);
复制代码
静态变量box5初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->5
执行第40行代码,对成员变量box3进行初始化
Box box3 = new Box(3);
复制代码
变量box3初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->3
2-2.对room对象实例化
执行Room构造方法:
public Room(){
System.out.println("Room()");
box4.f1(2);
}
复制代码
输出:Room()
box4执行f1()方法
输出:f1--->2
3.进入main()方法,执行第3行:
System.out.println("创建Room对象");
复制代码
4.执行第4行代码:
new Room();
复制代码
无论创建多少个对象,静态数据都只占用一份存储区域,即:无论创建多少个对象,静态数据只初始化一次
4-1.执行第40行代码,对成员变量box3进行初始化
Box box3 = new Box(3);
复制代码
变量box3初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->3
4-2.对room对象实例化
执行Room构造方法:
public Room(){
System.out.println("Room()");
box4.f1(2);
}
复制代码
输出:Room()
box4执行f1()方法
输出:f1--->2
5.执行第5行:
System.out.println("创建Room对象");
复制代码
6.执行第6行代码:
new Room();
复制代码
6-1.执行第40行代码,对成员变量box3进行初始化
Box box3 = new Box(3);
复制代码
变量box3初始化,执行第15~18行代码:
Box(int flag)
{
System.out.println("Box--->"+flag);
}
复制代码
屏幕输出:Box--->3
6-2.对room对象实例化
执行Room构造方法:
public Room(){
System.out.println("Room()");
box4.f1(2);
}
复制代码
输出:Room()
box4执行f1()方法
输出:f1--->2
7.执行第7行代码:
table.f2(1);
复制代码
输出: f2--->1
8.执行第8行代码:
room.f3(1);
复制代码
输出:f3--->1
结论:
-
静态变量只初始化一次
-
初始化顺序: 静态变量>变量>构造方法
五、显式的静态初始化
public class OrderOfStaticBlockTest {
public static void main(String[] args) {
System.out.println("Inside main");
Cups.cup2.f(99);
Cups cups = new Cups();
}
}
class Cup{
Cup(int flag)
{
System.out.println("Cup--->"+flag);
}
void f(int flag)
{
System.out.println("f--->"+flag);
}
}
class Cups{
static Cup cup2;
static{
cup2 = new Cup(2);
}
{
Cup cup1 = new Cup(1);
}
public Cups() {
Cup cup3 = new Cup(3);
}
}
复制代码
六、非静态实例初始化
public class OrderOfStaticBlockTest {
public static void main(String[] args) {
Cups cups = new Cups();
}
}
class Cup{
Cup(int flag)
{
System.out.println("Cup--->"+flag);
}
}
class Cups{
Cup cup1;
static Cup cup2;
static Cup cup4 = new Cup(4);
Cup cup5 = new Cup(5);
static{
cup2 = new Cup(2);
//cup5 = new Cup(5); 静态代码块只能执行静态变量
}
{
cup1 = new Cup(1);
}
public Cups() {
Cup cup3 = new Cup(3);
}
}
复制代码
运行结果:
Cup--->4
Cup--->2
Cup--->5
Cup--->1
Cup--->3
总结:
初始化运行顺序
静态变量>静态代码块>变量>代码块>构造方法
七.数组初始化
数组:相同类型的,用一个标识符名称封装到一起的一个对象序列或基本数据类型序列。
int[] arr;
复制代码
此时拥有的只是一个被存放在栈内存的引用,为了给数组在堆内存创建相应的存储空间,需要进行初始化。
1.确定数组元素内容的情况下:
int[] arr = {1,2,3,4,5};
复制代码
2.不确定数组元素内容的情况下:
int[] arr = new int[5];
复制代码
每个数组都有一个固定的属性length用于表示数组长度。数组的下标从0开始,一直到length-1。
八、可变参数列表
eg:
public class VariableDemoTest {
/**
* Java 5之前,实现可变参数列表
* @param objects
*/
void beforeVersionFive(Object[] objects)
{
for(Object obj : objects)
{
System.out.print(obj);
}
}
/**
* Java 5之后,实现可变参数列表
* @param objects
*/
void afterVersionFive(Object... objects)
{
for(Object obj : objects)
{
System.out.print(obj);
}
}
@Test
public void test() throws Exception {
Object[] objects1 = {};
Object[] objects2 = {"Hello"," ","World","!"};
beforeVersionFive(objects1);
afterVersionFive(objects1);
beforeVersionFive(objects2);
afterVersionFive(objects2);
}
}
复制代码
可变参数列表导致重载过程复杂化
public class OverrideVariableDemoTest {
public static void method(char...charactors)
{
for(char c : charactors)
{
System.out.println(c);
}
}
//编译失败:Java编译器不允许带可变参数的方法和带数组参数的方法在同一类中重载
// public static void method(char[] charactors)
// {
// for(char c : charactors)
// {
// System.out.println(c);
// }
// }
//编译失败:Java编译器不允许带相同参数类型列表的参数列表的方法在同一类中重载
// public static void method(char c,char... charactors)
// {
// for(char c1 : charactors)
// {
// System.out.println(c1);
// }
// }
//当可变参数方法与不带参数的方法重载时,JDK默认调用的是无参数的方法。
public static void method()
{
System.out.println("无参方法");
}
public static void main(String[] args) {
method();
}
}
复制代码
注意事项:
1.Java编译器不允许带可变参数的方法和带数组参数的方法在同一类中重载
2.Java编译器不允许带相同参数类型列表的参数列表的方法在同一类中重载
3.当可变参数方法与不带参数的方法重载时,JDK默认调用的是无参数的方法
九、枚举
枚举初始化
public enum ColorEnum {
RED,ORANGE,YELLOW,GREEN,BLUE,PINK,WHITE,BLACK;
}
复制代码
枚举的toString()方法会打印出它们的名字
ordinal()方法会获取它们的顺序
主要用途:
在switch语句中使用
eg:
public class EnumDemoTest {
@Test
public void test() throws Exception {
Color color = new Color(ColorEnum.ORANGE);
color.draw();
}
}
class Color
{
private ColorEnum cEnum;
public Color(ColorEnum cEnum)
{
this.cEnum = cEnum;
}
public void draw()
{
switch(cEnum)
{
case RED:
System.out.println("红色");
break;
case ORANGE:
System.out.println("橙色");
break;
case BLACK:
System.out.println("黑色");
break;
case YELLOW:
System.out.println("黄色");
break;
}
}
}
复制代码