继承
- 继承使用
extends
关键字
Object
- 任何类最终都继承自
java.lang.Object
,一般称之为基类;
同名的成员变量
- 子类可以定义与父类同名的成员变量,但不推荐这么做;
方法的重写
- 子类的方法签名与父类的一样,叫做方法的覆盖,重写;
- 子类重写的方法返回值类型必须 <= 父类的方法返回值类型;
- 子类重写的方法权限 >= 父类的方法权限;
super
- 访问父类的成员变量;
- 调用父类中定义的方法,包括构造方法;
构造方法的细节
- 子类的构造方法,必须
先调用父类的构造方法
,然后再执行后面的代码; - 案例代码:定义两个类Student与Person且Student继承自Student
public class Person {
public Person () {
System.out.println("Person()");
}
}
public class Student extends Person{
public Student () {
System.out.println("Student()");
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student student = new Student();
}
}
- 发现new Student(),依然会首先调用父类Person的构造方法;
- 如果子类的构造方法没有显示调用父类的构造方法,编译器会自动调用父类无参的构造方法,若此时父类没有无参的构造方法,编译器将报错;
public class Person {
public Person () {
System.out.println("Person()");
}
public Person (int age) {
}
}
- new Student(),会首先调用父类Person的构造方法
public Person ()
,Person现还有一个构造方法public Person (int age)
,若此时将
public Person ()
删除,程序将会报错,因为new Student(),会首先调用父类Person的构造方法,发现找不到目标方法;还可以改成Student构造方法,调用父类的构造方法;
public class Student extends Person{
public Student () {
super(0);
System.out.println("Student()");
}
}
注解Annotation
- 两个常见的注解:
- @override:告诉编译器这是一个重写后的方法;
- @SuppressWarnings("警告类别"):让编译器不生成警告信息;
- student变量没有被使用,会被警告,消除警告,可选中student,点击
command + 1
,出现如下所示:
- 最后成如下所示,消除了警告:
访问控制
- Java中有4个级别的访问权限,从高到低如下所示:
-
public
:在任何地方都是可见的; -
protected
:仅在自己的包中,自己的子类中可见; -
无修饰符(package-private)
:仅在自己的包中可见; -
private
:仅在自己的类中可见;
-
- 使用注意点:
- 上述4个访问权限都可以修饰类的成员,比如成员变量,方法,嵌套类等;
- 只有
public
,无修饰符(package-private)
可以修饰顶级类(Top-level Class) - 上述4个权限不可以修饰局部类,局部变量;
- 一个Java源文件中可以定义多个顶级类,public顶级类的名字必须和文件名一样;
- 在Java中,可以类嵌套,即A类中可以定义一个B类,那么最外层的类A,就可被称之为顶级类;
封装
- 在Java中封装:是指类的成员变量私有化,只能在本类中访问,提供setter,getter方法让外界进行访问,如下所示:
package com.lyy.model;
public class Person {
private String name;
private int age;
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
- 生成getter,setter方法的快捷键
shift + command + S
,或者
编辑区右击 -> source -> 选择生成getter,setter方法
Static
static:常用来修饰类的成员,包括成员变量,方法,嵌套类;
-
成员变量被static修饰时,
- 称之为类变量,静态变量,其在程序运行过程中只占用一份固定的内存(存储在方法区),可达到数据共享;
- 可以通过
类名
,实例对象
进行访问;
-
成员变量没有被static修饰时,
- 称之为实例变量,在每个实例对象内部都有一份;
- 只能通过实例对象进行访问;
-
方法被Static修饰时,称之为类方法,静态方法,通过类名进行调用;
- 可以通过
类名
,实例对象
进行访问; - 内部不可以使用this关键字;
- 可以直接访问类方法,类变量;
- 不可以直接访问实例方法,实例变量;
- 上述的规则都可以从this的本质进行解释;
- 可以通过
-
方法没有被static修饰时,称之为实例方法;
- 只能通过实例对象进行访问,不可以通过类进行访问;
- 内部可以使用this;
- 可以直接访问实例变量,实例方法;
- 可以直接访问类变量,类方法;
虽然可以通过实例对象去访问类变量与类方法,但一般情况下不推荐这么使用,还是使用类名去访问类变量与类方法;
静态导入
- 使用静态导入后,就可以省略类名来访问静态成员(成员变量,方法,嵌套类)
- 案例代码:
package com.sf;
import static com.sf.Person.*;
public class Main {
public static void main(String[] args) {
Person.age = 20;
System.out.println(Person.age);
Person.run();
//使用静态导入后 可省略类名直接访问
age = 10;
System.out.println(age);
run();
}
}
-
import static com.sf.Person.*;
属于静态导入; - 静态导入的经典使用场景:
package com.sf;
import static java.lang.Math.PI;
public class Main {
public static void main(String[] args) {
System.out.println(2 * PI * 10);
}
}
- 省略类名,可
直接使用PI
成员变量的初始化
- 编译器会自动为未初始化的成员变量设置初始值;
- 手动给实例变量进行初始化:
- 在声明中;
- 在构造方法中;
- 在初始化块中;
- 在初始化块中,编译器会将初始化块复制到每个构造方法的头部,
每创建一个实例对象,就会执行一次初始化块
; - 案例代码:
package com.sf;
public class Person {
//声明
public int age = 10;
public String name = "yanzi";
//构造方法
public Person () {
age = 10;
name = "yanzi";
}
//初始化块
{
age = 100;
name = "yanzi33";
}
}
- 手动给类变量进行初始化:
- 在声明中;
- 在静态初始化块中;
- 当一个类被初始化时 执行静态初始化块,
只会执行一次
; - 当一个类第一次被主动使用时,JVM会对类进行初始化;
- 案例代码:
package com.sf;
public class Person {
//声明
public static int age = 10;
//静态初始化块
static {
age = 100;
}
}
- 可以有多个(静态)初始化块,按照在源码中出现的顺序被执行;
- 案例代码:
package com.sf;
public class Person {
//静态初始化块
static {
System.out.println("Person -- static");
}
//初始化块
{
System.out.println("Person -- init");
}
//构造方法
public Person() {
System.out.println("Person -- 构造");
}
}
package com.sf;
public class Student extends Person{
//静态初始化块
static {
System.out.println("Student -- static");
}
//初始化块
{
System.out.println("Student -- init");
}
//构造方法
public Student() {
System.out.println("Student -- 构造");
}
}
package com.sf;
public class Main {
public static void main(String[] args) {
Student student = new Student();
}
}
- 执行顺序为:
- 类的初始化,实例的初始化,构造方法的调用都是首先完成父类的,然后再执行子类的;
单例模式
- 如果一个类设计成单例模式,那么在程序运行过程中,这个类只能创建一个实例对象;
- 饿汉式单例模式:
/**
* 饿汉式单例模式
*/
public class YYRocket {
//私有的静态的 实例变量
private static YYRocket instance = new YYRocket();
//构造方法私有化
private YYRocket() {}
//公共的静态方法,返回唯一的实例对象
public static YYRocket getInstance() {
return instance;
}
}
- 懒汉式单例模式
public class YYRocket {
//私有的静态的 实例变量
private static YYRocket instance = null;
//构造方法私有化
private YYRocket() {}
//公共的静态方法,返回唯一的实例对象
//存在线程安全问题
public static YYRocket getInstance() {
if (instance == null) {
instance = new YYRocket();
}
return instance;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
YYRocket rocket1 = YYRocket.getInstance();
YYRocket rocket2 = YYRocket.getInstance();
YYRocket rocket3 = YYRocket.getInstance();
Log.v("YYRocket",String.valueOf(rocket1 == rocket2));
Log.v("YYRocket",String.valueOf(rocket1 == rocket3));
Log.v("YYRocket",String.valueOf(rocket2 == rocket3));
}
}
- 打印结果为true,推荐使用饿汉式单例模式,效率高;
final
- final可以修饰类,被final修饰的类,不能被继承;
- final可以修饰方法,被final修饰的方法,不能被子类重写;
- final可以修饰变量,被final修饰的变量,只能进行一次赋值,可视作常量;
常量
- 常量的写法:
public class YYPerson {
public static final double HEIGHT = 172.5;
}
- static final 一起修饰常量,常量通常大写;
- 如果将基本类型或字符串定义为常量,并且在
编译时就能确定值
,那么编译器会使用常量值替代各处的常量名,类似于C语言的宏替换,通常称为编译时常量;
public class MainActivity extends AppCompatActivity {
static final int AGE = 100;
static final String name = "yanzi";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("MainActivity",String.valueOf(AGE));
Log.v("MainActivity",String.valueOf(100));
Log.v("MainActivity",name);
Log.v("MainActivity","yanzi");
}
}
嵌套类
- 嵌套类:定义在另一个类中的类;
public class OuterClass {
//非静态嵌套类 (内部类)
class InnerClass {
}
//静态嵌套类
static class StaticInnerClass {
}
}
- InnerClassy与StaticInnerClass都可称之为嵌套类;
- 非静态嵌套类,可称之为内部类InnerClass;
- 在嵌套类外层的类,称之为外部类OuterClass;
- 最外层的外部类,称之为顶级类;
内部类
- 没有被static修饰的嵌套类,即非静态嵌套类;
- 内部类与实例变量,实例方法一样,其与外部类的实例对象相关联,也就是说要想使用内部类,首先必须外部类实例化对象,
- 内部类不能定义除
编译时常量
以外的任何static成员; - 内部类可以直接访问外部类中所有成员,即使被声明为private;
- 外部类可以直接访问内部类的成员,即使被声明为private;
- 案例代码一:
package com.example.java_test.java;
import android.util.Log;
public class Person {
private int age;
public int getAge() {
return age;
}
//非静态嵌套类 (内部类)
public class Hand {
private double height;
}
}
import com.example.java_test.java.Person;
import com.example.java_test.java.Person.Hand;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Person person = new Person();
//通过外部类实例 创建内部类实例对象
Hand hand1 = person.new Hand();
Hand hand2 = person.new Hand();
}
}
- 内存布局如下:
- 案例代码一:
package com.example.java_test.java;
import android.util.Log;
public class Person {
private int age;
public int getAge() {
return age;
}
//访问内部类private成员
public void run(Hand hand) {
Log.v("Hand",String.valueOf(hand.height));
}
//非静态嵌套类 (内部类)
public class Hand {
private double height;
//编译时常量
private static final int width = 10;
//访问外部类private成员
public void show() {
Log.v("Hand",String.valueOf(age));
}
}
}
- 内部类的细节:
import android.util.Log;
public class OuterClass {
private int x = 1;
public class InnterClass {
private int x = 2;
public void show() {
Log.v("InnterClass",String.valueOf(x)); //2
Log.v("InnterClass",String.valueOf(this.x)); //2
Log.v("InnterClass",String.valueOf(OuterClass.this.x)); //1
}
}
}
静态嵌套类
- 静态嵌套类:被static修饰的嵌套类;
- 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中;
public class YYPerson {
public static class YYCar {
}
}
import com.example.java_test.java.YYPerson.YYCar;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
YYPerson person = new YYPerson();
//两种创建方式等价
YYPerson.YYCar car = new YYPerson.YYCar();
YYCar car = new YYCar();
}
}
- YYPerson与YYCar两者之间没有必然联系,所以说静态嵌套类在行为上就是一个顶级类;
- 对比一般的顶级类,静态嵌套类多了一些特殊的权限;
- 可以直接访问外部类中成员,即使被声明为private;
- 嵌套类的使用场景:
- 如果类A只用在类C内部,可以考虑将类A嵌套到类C中;
- 封装性更好;
- 程序包更加简化;
- 增强可读性,维护性;
- 如果类A需要经常要访问类C的非公共成员,可以考虑将类A嵌套到类C中;
- 另外也可以根据需要将类A隐藏起来,不对外暴露;
- 如果类A只用在类C内部,可以考虑将类A嵌套到类C中;
- 如果需要经常访问非公共的实例成员,设计成内部类,否则设计成静态嵌套类;
- 如果必须先有C实例,才能创建A实例,那么可以考虑将类A嵌套到类C中;
局部类
- 局部类:定义在代码块中的类(可以定义在方法中,for循环中,if语句中等等);
- 局部类不能定义除编译时常量以外的任何static成员;
- 局部类只能访问final 或者 有效final的局部变量;
- 从Java8开始,如果局部变量没有被第二次赋值,就可认定为有效final;
- 局部类可以直接访问外部类中的所有成员,即使被声明为private;
- 局部类只有定义在实例相关的代码块,才能直接访问外部类中的实例成员;
import android.util.Log;
public class YYPerson {
private int width;
//静态初始化块 与 类相关
static {
class A {
}
}
//初始化块 与实例相关
{
class A {
}
}
public void test() {
//有效final
int a = 10; //赋值会报错
//final
final int b = 11;
for (int i = 0; i < 10; i++) {
class A {
}
}
if (true) {
//局部类
class A {
void test() {
Log.v("test",String.valueOf(a));
Log.v("test",String.valueOf(b));
Log.v("test",String.valueOf(width));
}
}
}
}
}
抽象方法
- 抽象方法:被abstract修饰的方法;
- 只有方法声明,没有方法实现;
- 不能时private权限(因为定义抽象方法的目的是让子类去实现)
- 只能是实例方法,不能是类方法;
- 只能定义在抽象类,接口中;
抽象类
- 抽象类:被abstract修饰的类;
- 定义抽象类的目的是给其他类继承的,所以不能用final修饰;
- 可以定义抽象方法;
- 不能实例化,但可以自定义构造方法;
- 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类);
- 可以像非抽象类一样定义成员变量,常量,嵌套类型,初始化块,非抽象方法等;
- 使用场景:
- 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的定义成抽象方法;
public abstract class Shap {
protected double area;
protected double girth;
public double getArea() {
return area;
}
public double getGirth() {
return girth;
}
//抽象方法
abstract public void show();
}
package com.example.java_test.java;
import android.util.Log;
public class Rectangle extends Shap{
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void show() {
area = width * height;
girth = (width + height) * 2;
Log.v("Circle","area:" + String.valueOf(area)+ " girth:" + String.valueOf(girth));
}
}
import android.util.Log;
public class Circle extends Shap{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void show() {
area = Math.PI * radius * radius;
girth = Math.PI * radius * 2;
Log.v("Circle","area:" + String.valueOf(area)+ " girth:" + String.valueOf(girth));
}
}
import com.example.java_test.java.Circle;
import com.example.java_test.java.Rectangle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Circle circle = new Circle(10);
circle.show();
Rectangle rectangle = new Rectangle(10,20);
rectangle.show();
}
}