赞
踩
Java笔记目录可以点这里:Java 强化笔记
毕竟是进阶学习,挑了一些个人认为的易错点,难点,还有些太基础的我觉得是在没有必要记录了。。。
Java 中所有对象都是 new
出来的,所有对象的内存都是在堆空间,所有保存对象的变量都是引用类型。
public static void main(String[] args) {
Dog dog = new Dog();
dog.age = 20;
dog.weight = 5.6;
dog.run();
dog.eat("appel");
}
Java 运行时环境有个垃圾回收器(garbage collector,简称GC),会自动回收不再使用的内存
public class Dog {
public int price;
}
public class Person {
public int age;
public Dog dog;
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.price = 100;
Person person = new Person();
person.age = 20;
person.dog = dog;
}
public static void main(String[] args) {
Dog[] dogs = new Dog[7];
for (int i = 0; i < dogs.length; i++) {
dogs[i] = new Dog();
}
dogs[6] = null;
}
public class Dog { public int price; public void run() { System.out.println(price + "_run"); } public void eat() { System.out.println(price + "_eat"); } public static void main(String [] args) { Dog dog1 = new Dog(); dog1.price = 100; dog1.run(); dog1.eat(); Dog dog2 = new Dog(); dog2.price = 200; dog2.run(); dog2.eat(); } }
Java 虚拟机在执行 Java 程序时会将内存划分为若干个不同的数据区域,主要有:
this
是一个指向当前对象的引用,常见用途是:
只能在构造方法中使用 this
调用其他构造方法;
如果在构造方法中调用了其他构造方法:
this
的本质是一个隐藏的、位置最靠前的方法参数;
public class Dog { public String name; public int age; public int price; public Dog(String name, int age, int price) { this.name = name; this.age = age; this.price = price; } // 只能在构造方法中使用 this 调用其他构造方法 public Dog(String name) { // 如果在构造方法中调用了其他构造方法 // 构造方法调用语句必须是构造方法中的第一条语句 // int a = 1; //error this(name, 0, 0); } }
super
的常见用途是:
子类的构造方法必须先调用父类的构造方法,再执行后面的代码;
如果子类的构造方法没有显式调用父类的构造方法:
public class Person {
public int age;
public Person(int age) {
this.age = age;
}
}
public class Student extends Person {
public int no;
public Student(int no) {
super(0); // 不写会报错
this.no = no;
}
}
3 个常见的注解:
@Override
::告诉编译器这是一个重写后的方法@SuppressWarnings({"rawtypes", "ununsed"})
:让编译器不生成某个类别警告信息@Deprecated
::表示这个内容已经过期,不推荐使用Java 中有 4 个级别的访问权限,从高到低如下所示:
public
:在任何地方都是可见的protected
:仅在自己的包中、自己的子类中可见private
:仅在自己的类中可见
使用注意:
public
、无修饰符(package-private)可以修饰顶级类(Top-level Class)当打印一个对象时,会自动调用对象的 toString
方法,并将返回的字符串打印出来
toString
方法来源于基类 java.lang.Object
,默认实现如下所示:
默认打印:类名 + @ + 哈希值的16进制
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
static
常用来修饰类的成员:成员变量、方法、嵌套类
成员变量:(类变量,实例变量)
被 static
修饰:类变量,静态变量,静态字段
在程序运行过程中只占用一份固定的内存(存储在方法区)
可以通过实例、类访问
没有被 static
修饰:实例变量
在每个实例内部都有一份内存
只能通过实例访问,不可以通过类访问
不推荐使用实例访问类变量、类方法
方法:(类方法、实例方法)
static
修饰:类方法、静态方法static
修饰:实例方法在同一个类中:不能有同名的实例变量和类变量,不能有相同签名的实例方法和类方法。
static 有个用法:静态导入
静态导入的经典使用场景:
import static java.lang.Math.PI;
public class Main {
public static void main(String[] args) {
System.out.println(2 * PI * 10);
System.out.println(2 * PI * 20);
}
}
正确使用静态导入,可以消除一些重复的类名,提高代码可读性;
过度使用静态导入,会让读者分不清静态成员是在哪个类中定义的;
建议:谨慎使用静态导入。
编译器会自动为未初始化的成员变量设置初始值;
如何手动给实例变量提供初始值?
如何手动给类变量提供初始值?
public class Person { static { // 静态初始化块 System.out.println("static block"); } { // 初始化块 System.out.println("block"); } public Person() {} public Person(int age) {} public static void main(String[] args) { new Person(); // static block // block new Person(20); // block } }
一个更复杂的示例:
public class Person {
static {
System.out.println("Person static block");
}
{
System.out.println("Person block");
}
public Person() {
System.out.println("Person constructor");
}
public class Student extends Person {
static {
System.out.println("Student static block");
}
{
System.out.println("Student block");
}
public Student() {
System.out.println("Student constructor");
}
}
执行顺序:父类静态块 -> 子类静态块 -> 父类代码块 -> 父类构造器 -> 子类代码块 -> 子类构造器
public static void main(String[] args) {
new Student();
// Person static block
// Student static block
// Person block
// Person constructor
// Student block
// Student constructor
}
如果一个类设计成单例模式,那么在程序运行过程中,这个类只能创建一个实例。
饿汉式单例模式:像饿汉一样,上来就直接创建了唯一的那个实例。(线程安全)
/*
* 饿汉式单例模式
*/
public class Rocket {
private static Rocket instance = new Rocket();
private Rocket(){}
public static Rocket getInstance() {
return instance;
}
}
懒汉式单例模式:像懒汉一样,只有用到的时候采取创建实例。(线程不安全)
/*
* 懒汉式单例模式
*/
public class Rocket {
private static Rocket instance = null;
private Rocket(){}
public static Rocket getInstance() {
if (instance == null) {
instance = new Rocket();
}
return instance;
}
}
被 final
修饰的类:不能被子类化,不能被继承
被 final
修饰的方法:不能被重写
被 final
修饰的变量:只能进行1次赋值
常量的写法:
public static final double PI = 3.14159265358979323846;
private static final int NOT_FOUND = - 1;
如果将基本类型或字符串定义为常量,并且在编译时就能确定值:
例如下面的情况,编译时会被替换:
public class Main {
static final int A = 123456;
static final String B = "HELLO";
public static void main(String[] args) {
System.out.println(A); // 编译时直接被替换为下面
// System.out.println(123456);
System.out.println(B); // 编译时直接被替换为下面
// System.out.println("HELLO");
}
}
下面这种情况,编译时无法确定值,不会被替换:
public class Main {
static int NUMBER = getNum();
static int getNum() {
int a = 10;
int b = 20;
return a + b * 2 + 6;
}
public static void main(String[] args) {
System.out.println(NUMBER); // 不会被替换
}
}
public class OuterClass { // 顶级类
// 静态嵌套类
static class StaticNestedClass {
}
// 非静态嵌套类(内部类)
class InnerClass {
}
}
内部类:没有被 static
修饰的嵌套类,非静态嵌套类
跟实例变量、实例方法一样,内部类与外部类的实例相关联:
内部类与外部类:
private
)private
)内部类示例:先有公司 company
才能有员工 employee
,将 employee
设置为 company
的内部类,而 company
可以访问 employee
的实例的成员变量、方法(包括private
),employee
可以访问 company
的所有成员(包括 private
)。
public class Company { private String name; public Company(String name) { this.name = name; } public void fire(Employee e) { // 外部类可以直接访问[内部类实例]的成员变量(包括 private) System.out.println(name + " fire " + e.no); } public class Employee { private int no; public Employee(int no) { this.no = no; } public void show() { // 内部类可以直接访问[外部类]中的所有成员(包括 private) System.out.println(name + " : " + no); } } public static void main(String[] args) { Company c = new Company("Google"); Employee e = c.new Employee(17210224); e.show(); c.fire(e); } }
内部类的细节:如果有同名变量,默认访问内部的,访问外部的需要特别指出。
public class OuterClass { private int x = 1; // 外部类变量x public class InnerClass { private int x = 2; // 内部类变量x public void show() { // 默认访问内部 System.out.println(x); // 2 System.out.println(this.x); // 2 // 访问外部类的同名变量需要这么写 System.out.println(OuterClass.this.x); // 1 } } public static void main(String[] args) { new OuterClass().new InnerClass().show(); } }
public class Person { private int age; public class Hand { private int weight; } public static void main(String[] args) { // 必须先有Person对象才能创建Hand对象 Person p1 = new Person(); Hand h1 = p1.new Hand(); Person p2 = new Person(); Hand h2 = p2.new Hand(); } }
在 Hand
类被释放前,Person
类不会被释放(被 Hand
指向着)
静态嵌套类:被 static
修饰的嵌套类;
静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中;
对比一般的顶级类,静态嵌套类多了一些特殊权限
private
)public class Person { private int age; private static int count = 1; private static void run() { System.out.println("Person - run"); } public static class Car { // 静态嵌套类 public void test() { Person person = new Person(); // 静态嵌套类可以直接访问外部类中的成员(包括 private) System.out.println(person.age); // 0 Person.count = 1; Person.run(); // Person - run System.out.println(count); // 1 run(); // Person - run } } }
public static void main(String[] args) {
Person p = new Person();
// 静态嵌套类的使用
Person.Car c = new Person.Car();
// 如果之前 import Person.Car; 可以直接使用;
// Car c = new Car();
c.test();
}
如果类 A 只用在类 C 内部,可以考虑将类 A 嵌套到类 C 中;
如果类 A 需要经常访问类 C 的非公共成员,可以考虑将类 A嵌套到类 C 中;
如果需要经常访问非公共的实例成员,设计成内部类(非静态嵌套类),否则设计成静态嵌套类;
局部类:定义在代码块中的类(可以定义在方法中、for 循环中、if 语句中等)
局部类不能定义除编译时常量以外的任何 static
成员;
局部类只能访问 final
或者 有效final
的局部变量;
final
局部类可以直接访问外部类中的所有成员(即使被声明为 private
)
局部类示例:
public class TestLocalClass { private int a = 1; private static int b = 2; private static void test1() {} private void test2() {} public void test3() { int c = 2; class LocalClass { static final int d = 4; void test4() { System.out.println(a + b + c + d); test1(); test2(); } } new LocalClass().test4(); } }
抽象方法:被 abstract
修饰的实例方法
private
权限(因为定义抽象方法的目的让子类去实现)抽象类:被 abstract
修饰的类
常见使用场景:
实例:
public abstract class Shape {
protected double area;
protected double girth;
public double getArea() {
return area;
}
public double getGirth() {
return girth;
}
public void show() {
calculate();
System.out.println(area + "_" + girth);
}
protected abstract void calculate();
}
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
super();
this.width = width;
this.height = height;
}
@Override
protected void calculate() {
area = width * height;
girth = (width + height) * 2;
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
protected void calculate() {
double half = Math.PI * radius;
area = half * radius;
girth = half * 2;
}
}
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(10, 20);
rectangle.show();
Circle circle = new Circle(30);
circle.show();
}
API(Application Programming Interface)
Java 中的接口:
接口中可以定义的内容:
default
)、静态方法接口的细节:
一个类可以通过 implements
关键字实现一个或多个接口
extends
和 implements
可以一起使用,implements
必须写在 extends
的后面一个接口可以通过 extends
关键字继承一个或者多个接口
抽象类和接口的用途还是有点类似,该如何选择?
何时选择抽象类?
何时选择接口?
如果接口需要升级,比如增加新的抽象方法:会导致大幅的代码改动,以前实现接口的类都得改动
若想在不改动以前实现类的前提下进行接口升级,从 Java 8 开始,有 2 种方案:
default
修饰默认方法默认方法的使用:
当一个类实现的接口中有默认方法时,这个类可以:
当一个接口继承的父接口中有默认方法时,这个接口可以:
简单示例:Eatable
中有默认方法,Dog
啥也不干,Cat
覆盖默认方法。
public interface Eatable {
// 默认方法
default void eat(String name) {
System.out.println("Eatable - eat - " + name);
}
}
// Eatable接口中新增了方法, 但是没有影响到Dog类
public class Dog implements Eatable {}
// Eatable接口中新增了方法, Cat类中可以覆盖
public class Cat implements Eatable {
@Override
public void eat(String name) {
Eatable.super.eat(name);
System.out.println("Cat - eat - " + name);
}
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat("bone");
// Eatable - eat - bone
Cat cat = new Cat();
cat.eat("fish");
// Eatable - eat - fish
// Cat - eat - fish
}
1、如果父类定义的非抽象方法与接口的默认方法相同时,最终将调用父类的方法(就近原则):
public class Animal {
public void run() {
System.out.println("Animal - run");
}
}
public interface Runnable {
default void run() {
System.out.println("Runnable - run");
}
}
public class Dog extends Animal implements Runnable {}
public static void main(String[] args) {
Dog dog = new Dog();
dog.run(); // 继承的父类、实现的接口中都有 run() 方法, 默认调用父类的
// Animal - run
}
2、如果父类定义的抽象方法与接口的默认方法相同时,要求子类实现此抽象方法
super
关键字调用接口的默认方法public interface Runnable {
default void run() {
System.out.println("Runnable - run");
}
}
public abstract class Animal {
public void run() {}
}
public class Dog extends Animal implements Runnable {
// 父类的抽象方法run方法与接口中的run方法相同, 要求实现父类的抽象方法
@Override
public void run() {
Runnable.super.run(); // 可以通过super调用接口的默认方法
System.out.println("Dog - run");
// Runnable - run
// Dog - run
}
}
如果(父)接口定义的默认方法与其他(父)接口定义的方法相同时,要求子类型实现此默认方法:
例:Runnable
和 Walkable
两个父接口中定义的默认方法都是 run()
,Testable
继承了两个父类,则要求实现默认方法 run()
,Dog
类同理。
public interface Runnable {
default void run() {
System.out.println("Runnable - run");
}
}
public interface Walkable {
default void run() {
System.out.println("Walkable - run");
}
}
// Testable 父接口继承了 Runnable 父接口和 Walkable 父接口
// 他们都有默认方法 run, 要求 Testable 接口实现该默认方法
public interface Testable extends Runnable, Walkable {
@Override
default void run() {
Runnable.super.run();
Walkable.super.run();
System.out.println("Testable - run");
}
}
// Dog 类实现了 Runnable、Walkable 两个接口
// 他们都有默认方法 run, 要求 Dog 类实现该默认方法
public class Dog implements Runnable, Walkable {
@Override
public void run() {
Runnable.super.run();
Walkable.super.run();
System.out.println("Dog - run");
}
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.run();
// Runnable - run
// Walkable - run
// Dog - run
}
再看一个例子:
public interface Animal {
default String myself() {
return "I am an animal.";
}
}
public interface Fire extends Animal {}
public interface Fly extends Animal {
@Override
default String myself() {
return "I am able to fly.";
}
}
public class Dragon implements Fly, Fire {}
public static void main(String[] args) {
Dragon dragon = new Dragon();
System.out.println(dragon.myself());
// I am able to fly.
}
接口中定义的静态方法只能通过接口名调用,不能被继承
public interface Eatable {
static void eat(String name) {
System.out.println("Eatable - eat - " + name);
}
}
public interface Sleepable {
static void eat(String name) {
System.out.println("Sleepable - eat - " + name);
}
}
public interface Dog extends Sleepable, Eatable {
static void eat(String name) {
System.out.println("Dog - eat - " + name);
}
}
public static void main(String[] args) {
Dog.eat("1");
Eatable.eat("1");
Sleepable.eat("3");
// Dog - eat - 1
// Eatable - eat - 1
// Sleepable - eat - 3
}
什么是多态?
多态的体现:
JVM 会根据引用变量指向的具体对象来调用对应的方法:
多态示例:
public class Animal {
public void speak() {
System.out.println("Animal - speak");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Dog - wangwang");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Cat - miaomiao");
}
}
public static void main(String[] args) {
speak(new Dog()); // Dog - wangwang
speak(new Cat()); // Cat - miaomiao
}
// 多态的体现: 父类(接口)类型指向子类对象, 调用子类重写的方法
static void speak(Animal animal) {
animal.speak();
}
调用类方法只看左边声明的类,与实例化的类无关:
public class Animal {
public static void run() {
System.out.println("Animal - run");
}
}
public class Dog extends Animal{
public static void run() {
System.out.println("Dog - run");
}
}
public static void main(String[] args) {
Dog.run(); // Dog - run
Animal.run(); // Animal - run
Dog dog1 = new Dog();
// 调用类方法只看左边声明的类,与实例化的类无关
dog1.run(); // Dog - run
Animal dog2 = new Dog();
// 调用类方法只看左边声明的类,与实例化的类无关
dog2.run(); // Animal - run
}
public class Person {
public int age = 1;
public int getPAge(){
return age;
}
}
public class Student extends Person {
public int age = 2;
public int getSAge() {
return age;
}
}
public static void main(String[] args) {
Student stu1 = new Student();
System.out.println(stu1.age); // 2
System.out.println(stu1.getPAge()); // 1
System.out.println(stu1.getSAge()); // 2
Person stu2 = new Student();
System.out.println(stu2.age); // 2
System.out.println(stu2.getPAge()); // 1
}
instanceof
判断某个类型是否属于某种类型(及其子类)public class Animal {}
public interface Runnable {}
public class Dog extends Animal implements Runnable {}
public static void main(String[] args) {
Object dog = new Dog();
System.out.println(dog instanceof Dog); // true
System.out.println(dog instanceof Animal); // true
System.out.println(dog instanceof Runnable); // true
System.out.println(dog instanceof String); // false
}
instanceof
使用场景:
public class Animal {}
public class Cat extends Animal {
public void miao() {
System.out.println("Cat - miao");
}
}
public class Dog extends Animal{
public void wang() {
System.out.println("Dog - wang");
}
}
public static void main(String[] args) {
speak(new Dog()); // Dog - wang
speak(new Cat()); // Cat - miao
}
static void speak(Animal animal) {
if (animal instanceof Dog) {
((Dog) animal).wang();
} else if (animal instanceof Cat) {
((Cat) animal).miao();
}
}
public static void main(String[] args) {
Object obj1 = 11;
Integer obj2 = (Integer) obj1; // 可以转换成功
System.out.println(obj2);
// 下面一行等价于这么写: Object[] objs1 = new Object[] {11, 22, 33};
Object[] objs1 = { 11, 22, 33 };
// java.lang.ClassCastException:
// java.lang.Integer cannot be cast to [Ljava.lang.Integer
Integer[] objs2 = (Integer[]) obj1; // 转换失败, 抛异常
System.out.println(objs2);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。