一、关键字(static)
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
1.1 类属性(类变量)、类方法的设计思想
- 类属性作为该类各个对象之间共享的变量。在设计类时,分析那些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
- 如果方法与调用者无关,则这样的方法通常被声名为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用
1.1.1 类变量(Class Variable)
由该类的所有实例共享
1.1.2 类方法(Class Method)
- 没有对象的实例时,可以用类名.方法名()的形式访问由static标记的类方法
- 在static发方法内部只能访问类的static属性,不能访问类的非static属性
- 因为不需要实例就可以访问static方法,因此static方法内部不能有this也不能由super
- 重载的方法需要同时为static的或者非static的
1.2 使用范围
在Java类中,可用static修饰 属性、方法、代码块、内部类
1.3 被修饰后的特点
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
1.4 数据结构链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
1.5 链表代码实现
Node.class:
class Node {
int value;
Node next;
}
Link.class:
class Link {
// 头节点
private Node head;
// 尾节点
private Node tail;
// 添加
public void add(int val) {
// 把数据封到节点对象中
Node newNode = new Node();
newNode.value = val;
if (head == null) {
head = newNode;
tail = newNode;
} else {
tail.next = newNode;
tail = newNode;
}
}
// 遍历
public void travel() {
Node tmp = head;
while (tmp != null) {
System.out.print(tmp.value + " ");
tmp = tmp.next;
}
}
// 删除节点
public void remove(int val) {
Node prev = head; // 删除目标对象的前一个结点引用
System.out.println();
while (prev != null) {
// 找到了
if(prev.next.value == val) {
prev.next = prev.next.next;
System.out.println("删除成功!!");
break;
}
// 没找到
if(prev.next.value != val){
prev = prev.next;
}
// 找不到
if(prev == null) {
System.out.println("链表中不存在");
break;
}
}
// prev.next = prev.next.next;
}
// 链表大小
public int size() {
int x = 0;
Node tmp = head;
while (tmp != null) {
tmp = tmp.next;
x++;
}
return x;
}
}
FunnyTest:
public class FunnyTest {
public static void main(String[] args) {
Link link = new Link();
link.add(10);
link.add(2);
link.add(5);
link.add(8);
link.add(4);
link.add(7);
link.travel(); //10 2 5 8 4 7
System.out.println();
System.out.println("大小为 : " + link.size()); // 大小为 : 6
link.remove(2); // 删除成功!!
link.travel();// 10 5 8 4 7
System.out.println(); //
System.out.println("大小为 : " + link.size()); // 大小为 : 5
}
1.6 小结
- 多态 仅针对非静态方法而言
- 静态成员和覆盖,多态 无关
二、类的成员之四:初始化块
2.1 定义
一个类中初始化块若有修饰符,则只能被static修饰,称为静态代码块(static block ),当类被载入时,类属性的声明和静态代码块先后顺序被执行,且只被执行一次。
2.2 初始化块的作用
static块通常用于初始化static (类)属性
class Person {
public static int total;
static {
total = 100;//为total赋初值
}
…… //其它属性或方法声明
}
2.3 程序的执行顺序
- 声名成员变量的默认值
- 显式初始化、多个初始化块依次被执行(同级别下按先后顺序执行)
- 构造器再对成员进行赋值操作
2.4 静态代码块(static{语句})
1.可以有输出语句。
2.可以对类的属性、类的声明进行初始化操作。
3.不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
4.若有多个静态的代码块,那么按照从上到下的顺序依次执行。
5.静态代码块的执行要先于非静态代码块。
6.静态代码块只执行一次
2.5 非静态代码块({语句})
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
3.可以调用静态的变量或方法。
4.若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
5.每次创建对象的时候,都会执行一次。且先于构造器执行
2.6 访问
- 非静态成员之间可以直接互访
- 静态成员之间可以直接互访
- 非静态环境中可以直接访问静态成员
- 静态环境中不可以直接访问非静态成员,要想访问必须先创建对象,再通过对象访问
2.7 示例
Person.class:
class Person {
public static int total;
public int tmp;
// (静态代码块)程序开始时只执行一次且比非静态代码块先执行
static {
total = 100;
System.out.println("in static block!");
}
// (非静态代码块)每次创建对象时执行一次
{
this.tmp = total++;
}
}
Test.class:
public class Test {
public static void main(String[] args) {
// in static block!
System.out.println("total = "+ Person.total);// total = 100
System.out.println("total = "+ Person.total);// total = 100
Person p = new Person();
System.out.println(p.tmp); // 100
Person p1 = new Person();
System.out.println(p1.tmp); // 101
}
}
三、关键字:final
3.1 定义
在Java中声明类、属性和方法时,可使用关键字final来修饰,表示“最终”。
3.2 标记类
final标记的类不能被继承。提高安全性,提高程序的可读性。 如:String类、System类、StringBuffer类
final class A{
}
class B extends A{ //错误,不能被继承。
}
3.3 标记方法
final标记的方法不能被子类重写。如: Object类中的getClass()。
class A{
public final void print(){
System.out.println(“A”);
}
}
class B extends A{
public void print(){ //错误,不能被重写。
System.out.println("B");
}
}
3.4 标记变量
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。final标记的成员变量必须在声明的同时或在每个构造方法中或代码块中显式赋值,然后才能使用。如 : final double PI=3.14;
3.5 示例
public final class Test{
// 若是totalNumber此变量为static修饰的
// 那么会发现被final修饰的ID会通过每次创建对象而自增"this.ID = ++totalNumber"
// 有点像动态的final
public static int totalNumber;
public final int ID; // 必须赋值一次
public Test(){
// this.ID = ++totalNumber; //可在构造方法中给final变量赋值
// totalNumber = 10; // 静态变量也可在构造方法
}
// 静态语句块, 类加载时执行仅有的一次 , 天生的给静态的空final初始化
static{
totalNumber = 10;
}
// 非静态语句块, 在创建对象时执行仅有的一次, 且先于构造器执行,
// 并且无论是执行哪个构造器, 非静态语句块都要执行 天生的给非静态的空final初始化
{
this.ID = ++totalNumber; //可在非静态代码块中给final变量赋值
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.ID);
Test t1 = new Test();
System.out.println(t1.ID);
final int I = 10;
final int J;
J = 20;
// J = 30; // 不能赋值两次
}
}
3.6 小结
- 底层代码会把 非静态语句块, 显式赋值, 和构造器 合体, 形成一个特殊方法<init>
- 会把所有static{}和静态属性的显式同伴 合体, 形成一个特殊方法<cinit>
四、抽象类(abstract class)
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类就可以声明为抽象类。
4.1 抽象理解
- 具体类:对现实世界一种实体的抽象定义
- 抽象类:对现实世界某一类的多种不同实体的统一抽象定义
4.2 应用
- 用abstract关键字来修饰一个类时,这个类叫做抽象类
- 用abstract来修饰一个方法时,该方法叫做抽象方法(抽象方法:只有方法的声名,没有方法的实现。以分号结束),例如abstract int abstractMethod(int a);
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
- 不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。
- 超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
- 子类(具体类)可以继承抽象父类,实现抽象方法
- 具体子类使用抽象父类的多态
4.3 成员
- 属性
- 构造器
- 具体方法
- 抽象方法
4.4 规则
- 抽象类不能被实例化
- 子类(具体类)可继承抽象父类,实现抽象方法
- 具体子类使用抽象父类的多态
4.5 示例
Pet.java:
public abstract class Pet {
private String name;
private int age;
private double weight;
public Pet() {
super();
}
public Pet(String name, int age, double weight) {
super();
this.name = name;
this.age = age;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age + ", weight=" + weight;
}
// 抽象方法
abstract void sperak();
abstract void eat();
}
Bird.java:
public class Bird extends Pet{
private String flySpeed;
public Bird() {
}
public Bird(String name, int age, double weight, String flySpeed) {
super(name, age, weight);
this.flySpeed = flySpeed;
}
@Override
void sperak() {
System.out.println("我是小鸟我在叫");
}
@Override
void eat() {
System.out.println("我是小鸟我在吃");
}
@Override
public String toString() {
return super.toString() + ", flySpeed=" + flySpeed;
}
}
Cat.java:
public class Cat extends Pet{
private String color;
public Cat() {
}
public Cat(String name, int age, double weight, String color) {
super(name, age, weight);
this.color = color;
}
@Override
void sperak() {
System.out.println("我是猫我在叫");
}
@Override
void eat() {
System.out.println("我是猫我在吃");
}
@Override
public String toString() {
return super.toString() + ", color=" + color;
}
}
Dog.java:
public class Dog extends Pet{
private String breed;
public Dog() {
}
public Dog(String name, int age, double weight,String breed) {
super(name, age, weight);
this.breed = breed;
}
@Override
void sperak() {
System.out.println("我是狗我在叫");
}
@Override
void eat() {
System.out.println("我是狗我在吃");
}
@Override
public String toString() {
return super.toString() + ", breed=" + breed;
}
PetTest.java:
public class PetTest {
public static void main(String[] args) {
Pet[] pet = new Pet[6];
pet[0] = new Cat("小猫", 10, 5, "黄色");
pet[1] = new Cat("大猫", 11, 6, "白色");
pet[2] = new Dog("小狗", 13, 7, "黄色");
pet[3] = new Dog("大狗", 12, 20, "萨摩耶");
pet[4] = new Bird("小鸟", 4, 15, "快");
pet[5] = new Bird("大鸟", 6, 8, "慢");
// 选择排序
for (int i = 0; i < pet.length; i++) {
int minIndex = i;
for (int j = i + 1; j < pet.length; j++) {
if(pet[minIndex].getWeight() <= pet[j].getWeight()){
minIndex = j;
}
}
Pet tmp = pet[i];
pet[i] = pet[minIndex];
pet[minIndex] = tmp;
// 打印输出
System.out.println(pet[i].toString());
}
System.out.println();
}
/*
name=大狗, age=12, weight=20.0, breed=萨摩耶
name=小鸟, age=4, weight=15.0, flySpeed=快
name=大鸟, age=6, weight=8.0, flySpeed=慢
name=小狗, age=13, weight=7.0, breed=黄色
name=大猫, age=11, weight=6.0, color=白色
name=小猫, age=10, weight=5.0, color=黄色
*/
}
五、接口类(Inner class)
5.1 定义
有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。(一个类可以实现多个接口)
5.2 成员
5.2.1 属性
接口中的所有属性均被视静态常量。例如,下面几种方式的声明是等效的:
int num = 10;public int num = 10;public static final int num = 10
;
5.2.2 抽象方法
接口中所有方法均为抽象方法。例如,下面两种方式的声明是等效的:
public abstract void takeoff();public void takeoff()
;
5.3 用途
- 接口的用途是用来定义现实世界不同类型事物的共同行为特征。
- 接口中的所有方法均是抽象方法
- 主要用途就是被实现类实现(面向接口编程)
5.4 规则
- 接口不能被实例化
- 具体类(子类)可以实现接口(父类),并必须实现接口中的全部抽象方法。否则仍未抽象类
- class SubClass impllements InterfaceA{}
- 具体类适用父接口的多态
- 接口也可以继承其它接口,使用关键字extends
- 一个类可以同时继承父类并实现接口
5.5 定义小结
- 用interfacce来定义,被implements所实现
- 接口中的所有成员变量都默认是public static final修饰的
- 接口中的所有方法都是默认由public abstract 修饰的
- 接口没有构造器
- 接口采用多继承机制
5.6 使用小结
- 通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。
- 通过接口可以指明多个类需要实现的方法,一般用于定义对象的扩张功能。
- 接口主要用来定义规范。解除耦合关系。
5.7 接口与抽象类的区别
5.8 接口默认方法的"类优先"原则
- 条件 :若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
- 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
5.9 示例
Student.java:
package com.atguigu.javase.interfacetest;
public class Student implements Comparable{
private String name;
private int socre;
public Student() {
super();
}
public Student(String name, int socre) {
super();
this.name = name;
this.socre = socre;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSocre() {
return socre;
}
public void setSocre(int socre) {
this.socre = socre;
}
@Override
public String toString() {
return "Student [name=" + name + ", socre=" + socre + "]";
}
@Override
public int compareTo(Object o) {
if(o instanceof Student) {
return this.socre - ((Student)o).socre;
}
return 0;
}
}
SelectCompra.java:
class SelectCompra{
public static void SelectSort(Comparable[] arr) {
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
for (int j = i + 1 ; j < arr.length; j++) {
if(arr[j].compareTo(arr[minIndex]) < 0) {
minIndex = j;
}
}
Comparable tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
System.out.println(arr[i] + " ");
}
System.out.println();
}
}
SelectTest:
public class SelectTest {
public static void main(String[] args) {
String[] arr = new String[4];
arr[0] = "123";
arr[1] = "AAA";
arr[2] = "aaa";
arr[3] = "ttt";
SelectCompra.SelectSort(arr);
Student[] st = new Student[4];
st[0] = new Student("小二", 80);
st[1] = new Student("小三", 90);
st[2] = new Student("小四", 70);
st[3] = new Student("小五", 50);
SelectCompra.SelectSort(st);
/*
123
AAA
aaa
ttt
Student [name=小五, socre=50]
Student [name=小四, socre=70]
Student [name=小二, socre=80]
Student [name=小三, socre=90]
*/
}
}
六、内部类
6.1 定义
一个类的定义位于另一个类的内部
6.2 分类
6.2.1 成员内部类
声名在类中方法外
- 普通成员内部类 -> 无sataic修饰 -> Outher.inner o = new outher.new inner();
- 嵌套类 -> 有static修饰 -> 直接调用,不需要创建对象
6.2.2 局部内部类
声名在方法中
- 普通局部内部类 -> 方法中带类名的类
- 匿名内部类 -> 无类名 -> 通常用new 父类接口(){接口实现的方法}.接口实现方法();或者 -> 接口对象.父类接口实现方法();
new InterfaceSig() {
@Override
public void test() {
System.out.println("我是匿名内部类第一种表现形式");
}
}.test();
6.3 说明
1.可以调用外部类的结构
2. 可以用权限修饰符
3. 可以被static、final、abstract修饰
4. 可以被继承
5. 可以声明属性、方法、构造器
6.4 小结
你迷宫内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。匿名内部类一定是在new的后面,用其隐含实现一个接口或继承一个类
6.5 示例
Outher.java:
class Outher{
private int id = 10;
private String name = "Outher";
public class Inner1{
private int id = 100;
public void innerTest() {
System.out.println(Inner1.this.id); // 内部类的属性调用
System.out.println(Outher.this.id); // 外部类的属性调用
System.out.println(name);
outherTest2(); // 内部类直接调用外部类的方法
}
}
public void outherTest1() {
Inner1 inner1 = this.new Inner1(); // 创建一个内部内的对象
inner1.innerTest(); // 内部类对象执行内部类方法
}
public void outherTest2() {
System.out.println("外部类方法");
}
}
InnerClassTest.java:
public class InnerClassTest {
public static void main(String[] args) {
Outher o = new Outher();
// 用外部类对象new内部类对象
Outher.Inner1 oi = o.new Inner1();
}
}
七、Life
我在你眼中看到一丝寂寥
来源:CSDN
作者:SmallScorpion
链接:https://blog.csdn.net/qq_40180229/article/details/103503808