Java学习笔记(一)

重载函数

函数名称必须相同,函数的参数类型/个数必须有所不同
不能以返回值类型区分

public static int add(int a, int b)	{	}
public static int add(double a, double b) {	}	// 可与第一行函数重载
public static double add(int a, int b) {	}	// 不可重载

可变参数

概念:java允许将同一个类中多个同名且功能相同参数个数不同的方法,封装成一个方法。

基本语法:

  • 访问修饰符 返回类型 方法名(数据类型… 形参名)

例子:

// 使用方法重载
    // public int sum(int a, int b) {
    //     return a + b;
    // }
    // public int sum(int a, int b, int c) {
    //     return a + b + c;
    // }
    // public int sum(int a, int b, int c, int d) {
    //     return a + b + c + d;
    // }

    // 使用可变参数优化
    // ... 表示 可变参数,这里的 nums 可视为数组
    public static int sum(int... nums) {
        System.out.println("输入参数的个数:" + nums.length);
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        return sum;
    }

    public static void main(String[] args) {
        System.out.println(sum(1,465,789,123,46,564));
    }

注意事项:

  • 可变参数的实参可以是 0 或者任意多个
  • 可变参数的实参可以直接是数组,像调用刚刚那个方法 System.out.println( new int[]{1,465,789,123,46,564});,这样也是没问题的
  • 可变参数的实参实际上就是数组
  • 可变参数可以和普通类型参数一起放在形参列表,但是必须保证可变参数在最后
  • 一个形参列表只能出现一个可变参数

对象与基本类型:

新建一个对象相当于一个C语言中的指针,称为Reference
对象赋值是Reference赋值
而基本类型赋值是值拷贝

new出一个对象的默认值:

产生一个新的对象后,如果没有赋值,会有一个内部默认属性值:

类型默认值
short0
int0
long0
booleanfalse
char‘\u0000’
byte0
float0.0f
double0.0d

构造函数:

构造函数名称必须和类名一样,且没有返回值
每个Java类必须要有构造函数,否则编译器自动添加一个空的构造函数
可以有多个构造函数,只要形参列表不相同即可
构造函数只能通过new自动调用

构造函数内可以调用其他的成员方法,包括static关键字修饰的静态方法

信息隐藏:

类的成员属性是私有的 private
类的方法是共有的,通过方法修改属性值 public

this指针:

this指针负责指向:

  1. 本类中的成员变量
  2. 本类中的成员方法

也可以替代本类中的构造函数

super() 构造器

super() 是父类的构造函数。

  • 对于子类来说,如果构造函数的第一句不是 super(),编译器会自动增加一句 super()。如果构造函数第一句是程序员自己写的 super,那么不会自动添加。
  • 不只是限于无参的父类构造函数,有参数的 super 也可以。
  • 父类没有无参的构造函数,那么子类需要通过 super 指定使用父类的哪个构造函数。
  • this() 也只允许放在构造函数的第一行,因而其与 super() 在一个构造函数中只能存在一个。
  • 一个构造函数中只能有一个super
  • 父类构造器的调用并不是只有直接父类,而会一直往上回溯的基类 Object

public class A{
	public A(){
		System.out.println("dadadadadadad");
	}
	
	public A(int a){
		System.out.println("I am dadadadadadad");
	}
}

public class B extends A{
	public B(){
		// 编译器自动加super()
		System.out.println("son son son son");
	}
	
	public B(int a){
		// 编译器自动加super()
		super(6666);
		System.out.println("I am son son son son");
	}
	
	public static void main(String[] a){
		B obj1 = new B();
		System.out.println(">>>>>>>>>>>>>>>>>>>");
		B obj2 = new B(10);
	}
}

输出为:

dadadadadadad
son son son son
>>>>>>>>>>>>>>>>>>>
I am dadadadadadad
I am son son son son

如果class B定义修改为:

public class B extends A{
	public B(){
		// 编译器自动加super()
		System.out.println("son son son son");
	}
	
	public B(int a){
		super(a)
		System.out.println("I am son son son son");
	}
	
	public static void main(String[] a){
		B obj1 = new B;
		System.out.println(">>>>>>>>>>>>>>>>>>>");
		B obj2 = new B(10);
	}
}

那么输出变为:

dadadadadadad
son son son son
>>>>>>>>>>>>>>>>>>>
I am dadadadadadad
I am son son son son

继承

继承使用关键字 extends

class B extends A {}

上述例子即代表,类B 继承了 类A。那么 B 就称作子类或者派生类,A 就称作父类或者超类。

  • 子类拥有父类非私有(private)的所有的属性和方法
  • Java 是单继承机制,即一个类最多直接继承一个父类
  • 不能滥用继承,子类和父类应该满足 is-a 原则。例如 Cat extens Animal,显然 Cat is a Animal
  • Object 是顶层父类,所有类都是它的派生类
  • 子类必须调用父类的构造器(显示或者隐式),完成父类的初始化。
  • 当创建子类对象时,无论使用子类的哪个构造器,都会默认调用父类的无参构造器,除非子类构造器中显示的调用了父类的某一个构造器,或者是使用了 this() 指向了本类的另一个构造器

super 关键字

super 代表父类的引用,用于访问父类的属性、方法

  • 访问父类的属性,但不能访问父类的私有属性
  • 访问父类的方法,但不能访问父类的私有方法
  • 访问父类的构造器
  • 当子类的方法、属性和父类的重名时,需要使用 super 来指代父类的成员;如果没有重名,那么使用 super、this、直接访问效果都是一样的
  • super 的访问不限于直接父类,若爷爷类有和本类同名的成员,也可以访问爷爷类的成员。若多级父类都有该成员,则逐级查找,使用第一个也就是和本类继承关系最近的成员。

super 和 this 的比较

区别点thissuper
访问属性访问本类属性,本类没有则向上回溯从父类中查找从父类开始查找
调用方法访问本类属性,本类没有则向上回溯从父类中查找从父类开始查找
调用构造器调用本类构造器,必须在构造器首行调用父类构造器,必须在构造器首行
特殊表示当前对象子类中访问父类对象

方法重写

是指子类有一个方法,与父类的某个方法的名称、返回类型、参数都一样,这样就称子类重写(覆盖)了父类这个方法。

注意:

  • 子类的方法名、参数要和父类的完全一样
  • 子类的返回类型要么和父类的一样,要么是父类返回类型的子类。比如父类返回类型为 Object,子类可以是 Object,也可以是 String
  • 子类方法不能缩小父类方法的访问权限。如父类本身是 protected,子类可以是 public,但不可以是 private

方法重载与重写的比较

名称发生范围方法名形参列表返回类型修饰符
重载本类一致不一致无要求,都可无要求
重写子类与父类一致一致要么一致,要么子类返回类型是父类的返回类型的子类不可缩小访问权限

抽象类

只要用关键字abstract声明的类就是抽象类。
抽象类可以理解成由于父类方法的不确定性导致的,在当父类的某些方法需要声明,但是不确定如何实现时,可以将其声明为抽象方法,这个类就是抽象类

类: 属性(0或多个)+ 方法(0或多个)
完整的类: 所有的方法都有实现(方法体)
类可以没有方法,但是有方法就要实验,这样的类才是完整类,完整的类才可以实例化:被new出来

  • 抽象类指用关键字abstract声明的类
  • 抽象类必须使用关键字abstract声明,事实上只要用 abstract 修饰,抽象类中可以没有 abstract 方法
  • 抽象类的组成:成员变量、具体方法、抽象方法(使用关键字abstract声明,不一定有)。不过抽象类一般指含有抽象方法的类
  • 抽象方法不能有主体,即大括号
  • 抽象方法不能使用除 public 之外的修饰符修饰,这样才可以保证方法可以正常重写
  • 抽象类中也可以有静态成员和构造方法

子类可以继承抽象类,但是一定要实现父类所有的抽象方法。否则,子类也要定义为抽象类

接口

如果类的所有方法都未实现,这个类可被认为是接口interface,接口中,抽象方法可以省略关键字 abstract
不过,JDK 8 及之后的版本中,接口内也可以有实现的方法和静态方法(需要加关键字 default))。
辨别类和接口的主要区别就在于关键字,取决于声明的时候是 class 还是 interface

如:

public interface People {	// 一个接口
	public void eat();
	public void sleep();
	// jdk 8 以后可以有实现方法,但是需要加关键字 default
	default public void something() {
		System.out.println("do something");
	}
	// jdk 8 以后可以有静态方法,但是需要加关键字 default
	default static void anything() {
		System.out.println("do anything");
	}
}

public interface Climb {	// 一个接口
	public void climbTree();
}
// 继承了两个其它接口的接口
public interface Adult extends People, Climb {
}

所以,接口的构成为:

interface 接口名 {
	// 属性
	// 方法:1. 抽象方法  2. 默认的实现方法,加 default 关键字  3. 静态方法,加 default 关键字
}

接口可以继承多个接口,没有实现的方法会叠加。
类只可以继承(extends)一个类,但是可以实现(implements)多个接口。继承和实现可以同时出现。
例如:类C继承于类A,同时实现接口B,那么C的方法只会在A或者C中实现

接口可以继承(多个)接口,没有实现的方法将会叠加。
接口中可以定义变量,但一般是常量。

类实现(不是继承)接口,必须实现接口的所有方法。否则,类要定义为抽象类

public class Man implements People {
	
	public void eat(){
		System.out.println("I can eat");
	}
	
	public void sleep(){
		System.out.printlb("I can sleep");
	}
	
}
// 有方法未实现,定义为抽象类
public abstract class Baby implements People {
	// 抽象方法
	public abstract void eat();
	
	public void sleep() {
		System.out.printlb("I can sleep");
	}
}
// 继承加实现,注意extends需放在implements前面
public class Child extends Baby implements Climb {

	public void eat() {
		System.out.println("I can climb eat");
	}
	
	public void climbTree() {
		System.out.println("I can climb tree");
	}	
} 

public class Man implements Adult {

	public void eat() {
		System.out.println("I can climb eat");
	}
	public void sleep() {
		System.out.printlb("I can sleep");
	}
	public void climbTree() {
		System.out.println("I can climb tree");
	}
}

接口使用细节

  • 接口不能被实例化(接口类型变量可以指向实现了接口的对象实例)
  • 接口中所有的方法是 public 方法(即使不声明修饰符,也会默认是 public),接口中的抽象方法可以不用 abstract 修饰
  • 一个普通类实现接口,必须实现接口的所有方法
  • 抽象类实现接口,可以不实现接口的方法
  • 一个类可以同时实现多个接口
  • 接口中的属性只能是 final 的,而且是 public static final 修饰符(即使不声明修饰符,也会默认为这个),也就是一个常量
  • 接口中属性的访问方式:接口名.属性名
  • 一个接口不可以继承其他的类,但是可以继承多个别的接口
  • 接口的修饰符和类的修饰符一样,只能是 public 或者默认的

接口和继承的对比

当子类继承了父类,就会拥有父类的非私有的所有功能
而子类若需要扩展功能,就可以通过实现接口的方式进行扩展
实现接口就像是对Java单继承机制的一种补充

接口和继承解决的问题不同

  • 继承的价值:解决代码的复用性和可维护性
  • 接口的价值:在于设计,设计好各种规范,让其它类实现具体功能

接口比继承更加灵活

  • 接口比继承灵活性更高,继承满足 is - a 的关系,而接口只需满足 like - a 的关系

接口在一定程度上实现了代码解耦,即接口的规范性 + 动态绑定机制

接口的多态传递

假设现在有:接口 IA 和 接口 IB,其中 IB 继承了 IA,如果有一个类 CA 继承了 IB。那么可以有

IB ib = new CA();	// 这个无需多说
IA ia = new CA();	// 因为 IB 继承了 IA,所以这个也可,这就叫多态传递,类似于向下转型

封装

封装(encapsulation)就是把抽象出来的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序只能通过规定的操作(方法)才可以对数据进行操作。

封装的好处:

  • 隐藏方法的实现细节
  • 保护并验证数据

实现方法:

  1. 属性私有化 private 修饰属性
  2. 设置公共(public)的 get() 和 set() 方法,只能通过这两个方法获取属性。
  3. 在set()方法中,可以先加入数据的验证,保证合理再设置
  4. 在get()方法中,可以先加入权限的验证,用于保护数据

类转型

变量之间支持相互转化。
类之间可以相互转型,但是只限于有继承关系的类。

  • 子类可以转换为父类(从大变小,向上转型),父类不可以转换为子类(从小变大,向下转型,不允许)
  • 子类拥有父类所有的财产,子类可以变成父类
  • 转型后父类不可以访问子类特有的成员
	Human obj1 = new Man(); // Man extends Human, 这是对的
	Man obj2 = new Human(); // Man is a derived class of Human, 这是错的

有一种情况例外:父类本身就是子类转化过来的。

	Human obj1 = new Man(); // Man extends Human, 这是对的
	Man obj2 = (Man) obj1; // obj1 本身就是Man, 这是对的

子类继承父类的所有方法,但是子类可以重写(复写、覆盖,但不是重载)父类的方法,即重新定义一个名字、参数和父类一样的方法。
子类方法的优先级高于父类的。
子类转为父类后,调用共有的方法,依旧是执行子类中实现的方法。
例子:


public class Human {
    public void eat() {
        System.out.println("I can eat!");
    }
}

public class Man extends Human {
    public void eat() {
        System.out.println("I can eat more!");
    }
    public void plough() {
        System.out.println("I can plough!");
    }

    public static void main(String[] a) {
        Man obj1 = new Man();
        obj1.eat();
        Human obj2 = (Human)obj1;
        obj2.eat();
        Man obj3 = (Man)obj2;
        obj3.eat();
    }
}

输出结果是:

I can eat more!
I can eat more!
I can eat more!

可见三个对象的eat()方法都是子类Man中定义的那个。
obj2的eat()方法也为子类中定义的。因为子类中定义的方法优先级高于父类。且obj2是由obj1类转型过去的,实际上用的还是obj1的那块内存,不过属于子类的那部分方法在obj2中被隐藏了。
而obj3重新类转型为子类,所用的内存与obj1仍然相同,且无隐藏内容。
当使用判断语句时,会发现这三个对象实际上是相等的。

类型转换带来的作用就是多态。

多态

多态基本介绍

  • 方法和对象具有多种形态。其建立在封装和继承基础之上。

重载体现多态

  • 在一个类中使用重载实现多个同名方法,在调用时根据输入参数的不同而有不同的效果,即是多态的一种体现。

重写体现多态

  • 子类重写父类的方法,那么对于子类对象和父类对象,虽然都有一个方法名和参数相同的方法,却会有不同的效果

多态的作用:

  • 以统一的接口来操纵某一类中不同的对象的动态行为,即每个对象的行为由自己决定
  • 对象之间的解耦

例子:
首先定义一个接口和两个类

public interface Animal {
    
    public void eat();
    public void move();
    
}

public class Cat implements Animal {
    
    public void eat() {
        System.out.println("Cat can eat!");
    }
    public void move() {
        System.out.println("Cat can move!");
    }
}

public class Dog implements Animal {
    
    public void eat() {
        System.out.println("Dog can eat!");
    }
    public void move() {
        System.out.println("Dog can move!");
    }

}

然后写一个测试类


public class AnimalTest {
    public static void main(String[] args) {
        Animal[] as = new Animal[4];
        as[0] = new Cat();
        as[1] = new Dog();
        as[2] = new Cat();
        as[3] = new Dog();

        for (int i = 0; i < as.length; i++) {
            as[i].move(); // 调用每个元素自身的move方法
        }

        for (int i = 0; i < as.length; i++) {
            haveLunch(as[i]);
        }
    }

    public static void haveLunch(Animal a) {
        a.eat();
    }
}

输出结果是:

Cat can move!
Dog can move!
Cat can move!
Dog can move!
Cat can eat!
Dog can eat!
Cat can eat!
Dog can eat!

可见方法会因为类的不同而不同。

对象的多态

  • 对象的编译类型是定义对象时就确定的,不可改变
  • 对象的运行类型是可变的(通过类型转换)
  • 定义对象时,其 = 左边是编译类型,右边是运行类型
  • 对象的编译类型和运行类型可能不一致

例子:

Animal animal = new Dog(); // animal 的编译类型是 Animal,运行类型是 Dog
animal = new Cat(); // animal 的运行类型更改为 Cat

特别需要注意,属性没有重写之说

  • 属性的值看编译类型

instanceOf 比较方法,用于判断对象的运行类型是否为XX类型或XX类型的子类型。
这样就可以使用该方法来做运行类型未知的对象的向下转型。
如:

// 假设有父类 Person,子类 Man
Person[] p = new Person[5];
...
p[2] = new Man(); // 即数组 p 的第三个元素是 Man 对象
...
// 就可以在遍历的时候使用 instanceOf 方法
// 在当前元素运行类型是 Man 时,使用 Man 独有的方法,或者做一些特别的操作
for (int i = 0; i < 5; i++) {
	if (p[i] instanceOf Man) {
		//	do something
	}
	...
}

动态绑定机制

Java 的动态绑定机制是指:

  • 当调用对象方法的时候, 该方法会和该对象的内存地址/运行类型相绑定
  • 当调用对象属性的时候,没有动态绑定机制,使用的是编译类型对应的属性(哪里声明,使用哪里)

接下来通过三个来回的例子理解一下:

// 先定义两个类
public class A {
    public int i = 10;
    public int sum() {
        return getI() + 10;
    }

    public int sum1() {
        return i+10;
    }
    public int getI() {
        return i;
    }
}

public class B extends A{
    public int i = 20;

    @Override
    public int sum() {
        return i + 20;
    }

    @Override
    public int getI() {
        return i;
    }

    @Override
    public int sum1() {
        return i + 10;
    }
}

// 然用写一个测试类
public class Test {
    public static void main(String[] args) {
        A a = new B();  // 向上转型
        // a 的运行类型是 B
        // 接下来调用的方法是 B类 中的方法
        // 那么运行结果显而易见
        System.out.println(a.sum());    // 40
        System.out.println(a.sum1());   // 30

    }
}

紧接着上面的例子,如果 将子类 B 中的 sum() 方法注释掉,再运行会有不一样的结果。如下:

public class A {
    public int i = 10;
    public int sum() {
    	// B类 调用该方法,但是该方法使用了 getI() 方法,
    	// B类 中也有 getI(),于是触发动态绑定机制,调用 B类 的该方法
        return getI() + 10;
    }

    public int sum1() {
        return i+10;
    }
    public int getI() {
        return i;
    }
}

public class B extends A{
    public int i = 20;

    @Override
// 注释掉该方法
//    public int sum() {
//        return i + 20;
//    }

    @Override
    public int getI() { // 该方法返回本类的属性 i ,即 20
        return i;
    }

    @Override
    public int sum1() {
        return i + 10;
    }
}

// 然用写一个测试类
public class Test {
    public static void main(String[] args) {
        A a = new B();  // 向上转型
        // a 的运行类型是 B
        // B类 的sum()方法已经注释,所以这里调用的是 A类 的sum()方法
        // 在sum()中又调用了共有的getI()方法,触发动态绑定,使用B类的getI()
        System.out.println(a.sum());    // 30
        // sum1()方法不变
        System.out.println(a.sum1());   // 30

    }
}

那么,若再将 B类 的 sum1() 方法注释,如下:

public class A {
    public int i = 10;
    public int sum() {
    	// B类 调用该方法,但是该方法使用了 getI() 方法,
    	// B类 中也有 getI(),于是触发动态绑定机制,调用 B类 的该方法
        return getI() + 10;
    }

    public int sum1() {
    	// 属性无动态绑定,用的就是本类的属性
        return i+10;
    }
    public int getI() {
        return i;
    }
}

public class B extends A{
    public int i = 20;

    @Override
// 注释掉该方法
//    public int sum() {
//        return i + 20;
//    }

    @Override
    public int getI() { // 该方法返回本类的属性 i ,即 20
        return i;
    }

    @Override
//    public int sum1() {
//        return i + 10;
//    }
}

// 然用写一个测试类
public class Test {
    public static void main(String[] args) {
        A a = new B();  // 向上转型
        // a 的运行类型是 B
        // B类 的sum()方法已经注释,所以这里调用的是 A类 的sum()方法
        // 在sum()中又调用了共有的getI()方法,触发动态绑定,使用B类的getI()
        System.out.println(a.sum());    // 30
        // B类 的sum1()方法已经注释,所以这里调用的是 A类 的sum1()方法
        System.out.println(a.sum1());   // 20
    }
}

== 运算符 与 equals()方法

== 运算符可以用来判断基本类型是否相等,以及引用类型的地址是否相等

  • 如对于基本类型 3 == 4 // false10.0 == 10 // true
  • 对于引用类型,假设有类 Man 的两个对象 a , b, 只有两个对象指向同一块内存,即地址相同时返回 true

equals()方法用来判断引用类型的地址是否相等。它是 Object 类的方法。
但是其子类往往重写了该方法,比如字符串String的源码:

public boolean equals(Object anObject) {
        if (this == anObject) {	// 指向的地址若相同
            return true;
        }
        if (anObject instanceof String) {	// 是字符串
            String anotherString = (String)anObject;	// 向下转型
            int n = value.length;
            if (n == anotherString.value.length) {	// 挨个对比
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;	// 不是字符串直接false
    }

再看一下Integer的源码:

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

进行一个测试:

// 创建两个Integer对象
Integer c = new Integer(1000);
Integer d = new Integer(1000);
System.out.println(c == d);	// false 因为两个对象的地址不同
System.out.println(c.equals(d));	// true 值相同

hashCode()方法

该方法是Object类自带的方法,作用是返回某个对象的哈希码值。

  • 哈希码不同的对象一定不是同一个对象,即同一个对象的哈希码一定相同
  • 不同的对象哈希码可能相同,正常情况下都是不相同的

toString()方法

Object 也自带,基本类型一般会重写这个方法
在 Object 类中,它是用来返回调用对象的全类名(包名+ 类名)加上该对象的哈希值
若使用输出语句直接输出某个对象,而不指定属性或者方法,将会默认调用它的 toString() 方法
源码:

// getClass().getName() 获取全类名
// Integer.toHexString(hashCode()) 是将该对象的哈希码值转为16进制
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

finalize()

Object 类自带的方法。当某个对象不再有任何引用时,则 JVM 就会认为这是一个垃圾对象对其进行垃圾回收,在销毁前会先调用它的 finalize() 方法。
于是,可以通过重写该方法,在垃圾回收的时候填上自己需要的业务逻辑(如打开关闭其它文件,数据库操作,释放资源等)

不过需要注意,JVM有自己的垃圾回收算法,不是某个对象没有引用就会立即回收,何时回收是由系统决定的。但程序员也可以通过 System.gc() 主动触发垃圾回收机制(不是立即触发,也不是百分百触发,可能系统正好有别的安排)。
事实上,基本很少使用。只是面试会需要

契约设计

java编程设计遵循契约精神。
契约:规定规范了对象应该包含的行为方法。
接口定义了方法的名称、参数和返回值,规范了派生类的行为。
基于接口,利用转型和多态,不影响真正方法的调用,成功将调用类和被调用类解耦。
这里使用一个例子,是上一个多态例子的补充。

	// 调用类
	haveLunch(new Cat()); // Animal a = new Cat();  haveLunch(a);
    haveLunch(new Dog());
    haveLunch(
            new Animal() { // 使用Animal接口的匿名类,必须实现接口所有方法
                public void eat() {
                    System.out.println("Anonymous class can eat!");
                }
                public void move() {
                    System.out.println("Anonymous class can move!");
                }
            }
    );
   
	// 被调用类
    public static void haveLunch(Animal a) { 
        a.eat();
    }

调用类调用haveLunch,只需要传进来一个实现Animal接口的对象。
这样被调用类只用关心自己接收的是一个实现Animal接口的对象,而不关心对象本身的内容,只用按照接口的规则来开发实现自己的内容。
而调用类是实现接口的某个类,在被调用类执行时,这里有个向上转型的过程,a.eat()调用的其实是调用类的eat()方法。
根据调用类实现方法的不同,被调用类的执行结果也不同。
从而实现解耦。

Tips:

  1. double是浮点数,不精确,不能用于switch语句中
  2. main方法可以被重载、调用、继承和隐藏
  3. main方法的形参名字可以更改,但类型不可以更改
  4. JDK包含JRE,JDK是开发工具包,JRE是运行环境
  5. 接口所有方法都不能实现,且没有构造函数和main函数
  6. 抽象类可以有main函数
  7. 静态成员变量通常也叫做类变量,因为它不从属于任何对象。非静态的成员变量,也叫做实例变量,是从属于某一个具体的对象。
  8. 三元运算符是一个整体。如果有语句:Object obj = true ? new Integer(1) : new Double(2.0),然后对 obj 输出,结果会是 1.0。因为三元运算符是一个整体,类型上 Double 的优先级要高于 Integer,所以就给 1 转换成了 1.0 显示
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值