类的内部成员
一:属性(成员变量)
成员变量 VS 局部变量
成员变量和局部变量区别
- 在类中的位置不同
成员变量:在类中方法外面
局部变量:在方法或者代码块中,或者方法的声明上(即在参数列表中)- 在内存中的位置不同
成员变量:在堆中(方法区中静态区),成员变量属于对象,对象进堆内存
局部变量:在栈中,局部变量属于方法,方法进栈内存- 生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用或代码块的执行而存在,随着方法的调用完毕或者代码块的执行完毕而消失- 初始值
成员变量:有默认初始值
局部变量:没有默认初始值,使用前需赋值- 注意:
成员变量和局部变量的重名问题,就近原则;
可以使用this关键字区分,this.string指的是类中的成员变量,而不是方法内部的。- 相同点:
1.1 定义变量的格式:数据类型 变量名 = 变量值
1.2 先声明,后使用
1.3 变量都有其对应的作用域
如何声明
【修饰符】 数据类型 属性名;
【修饰符】 数据类型 属性名 = 初始值;//显式初始化
//如果在声明类时没有显式(手动)给属性赋初始值,那么在创建对象后,这个属性有默认的初始值
byte、short、int、long:0
float、double:0.0
char型:0或'\u0000',而非'0'(注意:此处0!!!)
boolean型:false
引用数据类型(类、接口、数组):null
封装
//公共的get/set
public class Student{
private String name;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
二:方法
基本概念
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
方法名:属于标识符,遵循标识符的规则和规范,“见名知意”
第一个单词首字母小写,其他单词首字母大写
四类方法
序号 | 链接 |
---|---|
1 | 无参无返回、无参带返回值、带参无返回值、带参带返回值 |
返回值类型: 返回值 vs 没返回值
- 要返回值,指定返回值的类型。使用return关键字来返回指定类型的变量或常量:“return 数据”。
- 没返回值,void来表示,不需要使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思。
特殊方法
静态方法:static
声明格式:权限修饰符 static 返回值类型 方法名(形参列表){
方法体}
静态方法中,只能调用静态的方法或属性
public static void show(){
System.out.println("我是一个中国人!");
// 不能调用非静态的结构
// eat();
// name = "Tom";
// 可以调用静态的结构
System.out.println(Chinese.nation);
walk();//静态方法
}
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
public void eat(){
System.out.println("中国人吃中餐");
//调用非静态结构
this.info();
System.out.println("name :" +name);
//调用静态结构
walk();
System.out.println("nation : " + nation);
}
注意
随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
在静态的方法内,不能使用this关键字、super关键字,因为不需要实例就可以访问static方法
抽象方法:abstract
声明一个方法但不提供实现,该方法的实现由子类提供
声明格式:【修饰符】 abstract 返回值类型 方法名(形参列表);
注意:
- 抽象方法只有方法的声明,没方法体
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所有抽象方法后,此子类方可实例化
- 若子类没重写父类中的所有抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
方法举例
方法重载
后续整理
方法重写
后续整理
方法的递归
递归方法:一个方法体内调用它自身
// 例1:计算1-n之间所自然数的和
public int getSum(int n) {
// 3
if (n == 1) {
return 1;
} else {
return n + getSum(n - 1);
}
}
// 例2:计算1-n之间所自然数的乘积:n!
public int getSum1(int n) {
if (n == 1) {
return 1;
} else {
return n * getSum1(n - 1);
}
}
//例3:已知一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),
//其中n是大于0的整数,求f(10)的值。
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
// return f(n + 2) - 2 * f(n + 1);
return 2*f(n - 1) + f(n - 2);
}
}
//例4:斐波那契数列
......
//例5:汉诺塔问题
......
//例6:快排
......
可变形参
举例:
public void show(int i){
}
public void show(String s){
System.out.println("show(String)");
}
public void show(String ... strs){
System.out.println("show(String ... strs)");
for(int i = 0;i < strs.length;i++){
System.out.println(strs[i]);
}
}、
调用时:
test.show("hello");
test.show("hello","world");
test.show();
test.show(new String[]{
"AA","BB","CC"});
方法参数传递
三:构造器(构造方法)
如何声明
(1)无参构造
【修饰符】 构造器方法名(){
方法体
}
(2)有参构造
【修饰符】 构造器方法名(形参列表){
方法体
}
特点
A:构造器方法名必须与类名相同
B:构造器没有返回值类型
C:构造器可以重载
D:任何类中都有构造器,如果没有显式/手动声明构造器,编译器将自动添加一个默认的无参构造器
E:如果显式/手动声明了任何一个构造器,编译器将不再自动添加默认的无参构造
作用
四:代码块
静态代码块
- 内部可以输出语句
- 随着类的加载而执行,而且只执行一次
- 作用:初始化类的信息
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行(一般定义一个即可!!多个代码块可以合并成一个)
- 静态代码块的执行要优先于非静态代码块的执行
- 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块
- 内部可以输出语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用:可以在创建对象时,对对象的属性等进行初始化
- 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行(一般定义一个即可!!多个代码块可以合并成一个)
- 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
执行特点和加载顺序
- 执行特点:静态代码块(只执行一次)–》构造块(每次创建对象) --》构造器(每次创建对象)
- 加载顺序:总结:由父及子,静态先行
- 涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序
五:内部类
根据声明的位置不同
成员内部类
- 如何声明:
【修饰符】 class 外部类{
【修饰符】 class 内部类{
}
}
-
如何使用:
(1)在外部类中使用内部类- 除了在外部类的静态成员中,不能直接使用内部类
- 外部类不允许直接访问非静态内部类中的实例变量。如需访问,必须显式创建内部类对象后才能用该对象访问
(2)在外部类的外面使用内部类
- 它需要外部类的对象,才能获得内部类的对象等
示例一:
class Outer{
class Inner{
}
}
//使用
Outer.Inner inner = new Outer().new Inner();
Outer out = new Outer();
Outer.Inner inner = out.new Inner();
示例二:
class Outer{
class Inner{
}
public Inner getInner(){
return new Inner();
}
}
//使用
Outer out = new Outer();
Outer.Inner in = out.getInner();
示例三:
class Outer{
class Inner{
}
}
//继承 Inner
MyClass需要调用Inner的构造器
而调用Inner的构造器,需要外部类Outer的对象
class MyClass extends Outer.Inner{
public MyClass(Outer out){
out.super();
}
}
(3)在内部类中使用外部类的成员
- 没有限制,都能用,包括静态的,包括私有的
其他特点:
静态内部类
如何声明:
【修饰符】 class 外部类{
【修饰符】static class 内部类{
}
}
(1)需要在内部类中声明静态成员,那么这个内部类必须是static
(2)需要在外部类的静态成员中使用这个内部类,那么这个内部类也必须是static
如何使用:
(1)在外部类中使用:没有限制
(2)在外部类的外面
示例一:
class Outer{
static class Inner{
public void test(){
}
}
}
//如何调用test()
(1)从test()看,它不是静态的,因此需要Inner的对象
(2)Inner在Outer里面,而Inner又是static修饰
Outer.Inner in = new Outer.Inner();
in.test();
示例二:
class Outer{
static class Inner{
public static void test(){
}
}
}
//使用test()
(1)看test(),是static修饰符,说明不需要Inner对象
(2)看Inner是static修饰,也不需要外部类的对象
Outer.Inner.test();
(3)在内部类中使用外部类的成员
- 在静态内部类中,不能使用外部类的非静态成员
- 其他的成员可以直接使用,包括私有的
其他特点
- (1)静态的内部类,可以使用权限修饰符 4种
- (2)静态的内部类可以包含静态的成员
- (2)静态的内部类可以包含静态的成员
局部内部类
如何声明:
【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(形参列表){
class 内部类名{
}
}
}
如何使用:
其他特点
- (1)不能使用public,protected,private,static这些成员修饰符
- (2)局部内部类中,也不能包含静态成员的
- (3)局部内部类也有自己的字节码文件:外部类$编号内部类.class
匿名内部类
如何声明
【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(形参列表){
new 父类/父接口(){
//内部类的成员
}
}
}
注意:此处,父类要注意是否有无参构造
如何使用
(1)使用父类的多态引用使用它对象
- 只能调用父类中声明过的方法
package com.atguigu.review;
//在main中,写一个Father的匿名内部类的对象
public class TestNiMing {
public static void main(String[] args) {
Father f = new Father("尚硅谷"){
/*@Override
public String getInfo() {
return "内部类的示例:" + getInfo();//java.lang.StackOverflowError
}*/
@Override
public String getInfo() {
return "内部类的示例:" + super.getInfo();
}
public void method(){
System.out.println("匿名内部类自己的方法");
}
};
System.out.println(f.getInfo());
// f.method();//无法访问
}
}
class Father{
private String info;
public Father(String info) {
super();
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
(2)本态引用调用方法
- 可以调用父类的方法,也可以调用子类自己的方法
public static void main(String[] args) {
//父类是Object,使用匿名内部类,声明一个方法void fun(),并调用
new Object(){
public void fun(){
System.out.println("匿名内部类的方法");
}
}.fun();
}
(3)作为方法的实参
package com.atguigu.review;
import java.util.Arrays;
import java.util.Comparator;
public class TestLocal3 {
public static void main(String[] args) {
Dog[] dogs = new Dog[3];
dogs[0] = new Dog(7.8);
dogs[1] = new Dog(5.8);
dogs[2] = new Dog(9.8);
Arrays.sort(dogs, new Comparator(){
@Override
public int compare(Object o1, Object o2) {
Dog d1 = (Dog) o1;
Dog d2 = (Dog) o2;
if(d1.getWeight() > d2.getWeight()){
return 1;
}else if(d1.getWeight()
return -1;
}
return 0;
}
});
}
}
class Dog{
private double weight;
public Dog(double weight) {
super();
this.weight = weight;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
其他特点
面向对象三大特征
封装性
封装两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个方面都需要通过使用Java提供的访问控制符来实现。
Java提供了3个访问控制符:private、缺省、protected和public,
关于访问控制符的使用,存在如下几条基本原则。
➢ 类里的绝大部分成员变量都应该使用private修饰,只有一些static修饰的、类似全局变量的成员变量,才可能考虑使用public修饰。除此之外,有些方法只用于辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰。
➢ 如果某个类主要用做其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法。
➢ 希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造器通过使用public修饰,从而允许在其他地方创建该类的实例。因为外部类通常都希望被其他类自由使用,所以大部分外部类都使用public修饰。
- 广义的封装
- 狭义的封装
继承
多态
- 多态的作用:代码更灵活
表现
(1)方法的多态性:方法的重写
(2)对象的多态性
对象的多态性:父类的引用指向子类的对象
Person p2 = new Man();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
p2.eat();
p2.walk();
多态的应用
(1)多态参数
方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法
public class Test {
public void method(Person e) {
//Person e = new Student()
e.getInfo();
}
public static void main(Stirng args[]) {
Test t = new Test();
Student m = new Student();
t.method(m); // 子类的对象m传送给父类类型的参数e
}
}
(2)多态数组
public static void main(String[] args)
(3)多态属性
一个引用类型变量如果声明为父类的类型,但实际引用的是子类
对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编
译错误
数据类型的转换
- 向上转型
- 向下转型
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的方法。子类特有的方法不能调用。
如何才能调用子类特的属性和方法?使用向下转型(强制转换)。
- 子类类型 变量 = (子类的类型)父类的变量;
public class Test {
public void method(Person e) {
// 设Person类中没有getschool() 方法
// System.out.pritnln(e.getschool()); //非法,编译时错误
if (e instanceof Student) {
Student me = (Student) e; // 将e强制转换为Student类型
System.out.pritnln(me.getschool());
}
}
}
关键字的使用
this:表示当前对象
(1)this.属性/this.方法
- 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式
- 在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,
- 在某个方法中把this作为返回值,则可以多次连续调用同一个方法,从而使代码更加简洁。
(2)this调用构造器
- ① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
- ② 构造器中不能通过"this(形参列表)"方式调用自己
- ③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)"
- ④ 规定:"this(形参列表)"必须声明在当前构造器的首行
- ⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器
super:表示父类的引用
(1)super.属性/super.方法
我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,
显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
- 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
(2)构造器:super()或super(实参列表)
super调用构造器:
1:我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
2:"super(形参列表)"的使用,必须声明在子类构造器的首行!
3: 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)“只能二一,不能同时出现
4:在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
5:在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)”,调用父类中的构造器
static:静态的
- static相当于一个标志,有static修饰的成员属于类本身,没有static修饰的属于该类的实例
- 修饰属性、方法、代码块、内部类
final:最终的
instanceof:
- 表示某个对象是否是本类或本类的子类的对象
extends:表示继承
抽象:abstract
abstract class 几何图形类{
public abstract double getArea();
}
class 圆 extends 几何图形类{
private double 半径;
public double getArea(){
return Math.PI * 半径 * 半径;
}
}
class 矩形 extends 几何图形类{
private double 长;
private double 宽;
public double getArea(){
return 长 * 宽;
}
}
原因:
(1)从逻辑角度
几何图形类中,应该包含所有几何图形共同的特征
那么所有几何图形,就应该包含 “获取面积”的功能
(2)语法角度
通过“几何图形”类对圆对象,矩形对象等的多态引用,
应该是可以调用“获取面积”的方法,如果父类中没有声明该方法,
就无法调用,无法实现多态引用
抽象类
在父类中明确子类应该包含某些方法,但是在父类中又不能给出具体的实现,父类中只能把这个方法声明为抽象方法
格式【修饰符】 abstract class 抽象类的名称{
类的成员列表
}
abstract class A {
abstract void m1();
public void m2() {
System.out.println("A类中定义的m2方法");
}
}
class B extends A {
void m1() {
System.out.println("B类中定义的m1方法");
}
}
public class RecursionTest {
public static void main(String args[]) {
A a = new B();
a.m1();
a.m2();
}
}
编译结果:
B类中定义的m1方法
A类中定义的m2方法
抽象方法
【修饰符】 abstract 返回值类型 方法名(形参列表);
- 声明一个方法但不提供实现,该方法的实现由子类提供
- 抽象方法只有方法的声明,没方法体
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所的抽象方法后,此子类方可实例化
- 若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
抽象类的应用
(1)模板设计模式:在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
abstract class Template{
//计算某段代码执行所需要花费的时间
public void spendTime(){
long start = System.currentTimeMillis();//开始时间
this.code();//不确定的部分、易变的部分
long end = System.currentTimeMillis();//结束时间
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for(int i = 2;i <= 1000;i++){
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++){
if(i % j == 0){
isFlag = false;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}
package:包
设计模式
单例设计模式
特征:
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
私有化类的构造器/避免在类的外面调用构造器造对象!并且类内部需要new一个对象
形式:
1:懒汉式:
2:饿汉式:
3:枚举式:
要点:
- 构造器私有化
- 在单例类中创建这个唯一的对象
模板设计模式
特征:
在父类中能确定某个功能的整体的算法结构,但是对于其中的某个步骤,在父类中无法确定,要延迟到子类中去实现,这个时候就可以通过模板设计模式。属于抽象类的应用
计算任意一段代码或功能的运行时间
//父类:确定算法结构
abstract class SuperClass{
public final long getTime(){
//1、获取开始时间
long start = System.currentTimeMillis();
//2、运行要计算时间的代码
run();
//3、获取结束时间
long end = System.currentTimeMillis();
//4、计算
return end - start;
}
protected abstract void run();
}
class SubClass extends SuperClass{
protected void run(){
//要测试时间的代码
}
}
工厂设计模式
目的:使得使用者与创建者分离,把创建某些接口的实现类对象交给工厂来做
简单工厂模式
优点:代码简洁
缺点:当增加新产品时,需要修改工厂方法,违反了“对修改关闭,对扩展开放”的原则
//1、所有的要创建的对象,都是符合某个标准的
interface Car{
void run();
}
class BMW implements Car{
public void run(){
....
}
}
class Benz implements Car{
public void run(){
....
}
}
//2、设计一个工厂类,用来创建各种各样的Car的实现类的对象
class CarFactory{
public static Car getCar(String name){
if("宝马".equals(name)){
return new BMW();
}else if("奔驰".equals(name)){
return new Benz();
}
return null;
}
}
工厂方法模式
优点:如果增加新产品,只需要增加对应的工厂即可,不需要修改原来的代码
缺点:类太多,代码复杂
//1、所有的要创建的对象,都是符合某个标准的
interface Car{
void run();
}
class BMW implements Car{
public void run(){
....
}
}
class Benz implements Car{
public void run(){
....
}
}
//2、每一种产品都自己的工厂生产
//所有的工厂就有共同的特征
interface Factory{
Car getCar();
}
class BMWFactory implements Factory{
public BMW getCar(){
return new BMW();
}
}
class BenzFactory implements Factory{
public Benz getCar(){
return new Benz();
}
}