第十章、面向对象
一、总结
变量/属性当赋值一次后,若未重新赋值,其值一直不变
方法每调用一次,就执行一次
变量/属性不存在调用,只有方法才能调用
二、面向过程与面向对象
1、面向过程(蛋炒饭 耦合度高,扩展力低)
主要关注点是:实现的具体过程,因果关系(集成显卡的开发思路)
- 优点:
- 对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低
- 缺点:
- 采用面向过程的方式开发很难解决非常复杂的业务逻辑,另外面向过程的方式导致软件元素之间的“耦合度”非常高,只要其中一环出现问题,整个系统受到影响,导致最终软件的扩展力差。另外,由于没有“独立体”的概念,所以无法达到组件复用
2、面向对象 object-oriented(盖饭 耦合度低,扩展力高)
面向对象:object-oriented 简称OO
主要关注点是:关注对象(独立体)能完成哪些功能(独立显卡的开发思路)
- 优点:
- 耦合度低,扩展力高,更容易解决现实世界当中更复杂的业务逻辑,组件复用性强
- 缺点:
- 前期投入成本高,需要进行独立体的抽取,大量的系统分析和设计
耦合度和扩展力的关系:反比关系,耦合度越高,扩展力越低
C语言是纯面向过程的,C++是半面向对象,Java是纯面向对象
现在出现的一些新的编程语言多数都是面向对象的,人在认识现实世界的时候以面向对象的方式,面向对象更符合人的思维方式
面向对象的三大特征:
- 封装
- 继承
- 多态
所有面向对象的编程语言都有这三大特征
采用面向对象的方式开发一个软件,声明周期当中:整个生命周期贯穿使用OO面向对象方式
- 面向对象的分析:OOA
- 面向对象的设计:OOD
- 面向对象的编程:OOP
3、类和对象的概念
类的定义:
- 类是现实世界当中不存在的,是一个模板,是一个概念。是人类大脑抽象的结果
- 类代表一类事物
- 在现实世界中,对象A和对象B之间具有共同的特征,进行抽象总结出一个模板,称为类
对象的概念
- 对象是现实世界中实际存在的个体。
软件开发的过程:
- 程序员位于现实世界与虚拟实践的中间点,通过类和对象将现实世界中的东西在虚拟世界实现
- 程序员先观察现实世界,从现实世界当中寻找对象
- 寻找了N多个对象后,发现所有对象都有共同特征
- 程序员在大脑中形成了一个模板(类)
- Java程序员可以通过Java代码来表述一个类
- Java程序中有了类的定义
- 可以通过类来创建对象
- 有了对象之后,可以让对象直接协作起来形成一个系统
类--(实例化)--》 对象
对象--(抽象)--》类
对象又被称为实例/instance
- 类描述的是对象的共同特征
- 在访问这些特征的时候,必须先创建对象
- 一个类主要描述的是:状态 + 动作
状态--》一个类的属性
动作--》一个类的方法
类{
属性;//描述对象的状态信息
方法://描述对象的动作信息
}
属性和方法具体到某个对象之后,可能最终的结果不一样
对象和对象之间有共同特征,但是具体到对象之后有数据的差异
第十一章、java中常用的修饰符关键字
java程序中,类是基本单位,方法是一般单位,成员变量是最小单位
修饰符以类级别为单位 修饰类中的子单位,子单位中不能有任何修饰符
包级别 > 类级别 > 实例级别
小级别可以直接访问大级别的,大级别不能直接访问小级别的
在Java中,有许多修饰符关键字用于限制类、方法、变量等的访问级别和特性。以下是常用的修饰符关键字:
一、default:纯包级私有,条件下等同于实例私有
若是在同一个包内的其他类中访问(默认等同于实例私有),在不同包中访问时必须加public
如果在Java中定义类,变量或方法时不加修饰符,它们将默认为使用Java中的默认访问修饰符。对于类的成员变量和方法,它们的默认访问修饰符是“包级私有”(package-private),也称为“default”修饰符。这意味着它们只能在同一个包内的其他类中访问(默认对象私有)。对于局部变量,没有默认修饰符,因为它们只能在定义它们的方法或代码块中使用。建议在定义变量和方法时显式指定访问修饰符,以确保程序的可读性和可维护性。
二、public:表示公开的,可以被任何类访问,主要用于打破包级私有,因为public使用import机制可以导包。
public关键字用于指定公开的访问级别,表示该类、方法或变量可以被其他任何类访问。
主要用于打破包级私有
public class MyClass {
public int myVariable;
public void myMethod() {
// do something
}
}
这里,MyClass类和其中的myVariable变量和myMethod()方法都被指定为public,所以它们可以被其他任何类访问。
在Java编程语言中,public是一种访问修饰符(Access Modifier),它可以用于类、方法、属性等的声明上。public关键字表示这些成员是公共的,可以被任何类、方法或对象调用和访问。
具体来说,使用public修饰的类、方法、属性等可以被任何其他类访问和调用,无论这些类是否在同一个包中。
如果一个类不在同一个包中,那么我们需要通过导入(import)语句来引入该类,才能够使用它。
使用public关键字修饰的类、方法、属性等可以被其他包中的类访问和调用,但是在使用时,需要确保以下两点:
- 在使用public成员的类中,需要通过import语句将所使用的类引入进来;
- 在被引入的类中,public成员的访问修饰符不能是protected、private或默认(即没有任何访问修饰符),否则在引入该类的外部无法访问这些成员。
需要注意的是,如果被引入的类是一个包级私有(默认)类,则在另一个包中无法访问该类。在这种情况下,只能在同一个包中的其他类中访问该类。
假设我们有两个类:com.example.package1.ClassA
和com.example.package2.ClassB
,它们分别位于com.example.package1
和com.example.package2
两个不同的包中。
ClassA
中有一个公共方法public void methodA()
,而ClassB
中想要使用这个方法。那么,我们需要在ClassB
中通过import语句将ClassA
引入进来,例如:
package com.example.package2;
import com.example.package1.ClassA;
public class ClassB {
public static void main(String[] args) {
ClassA a = new ClassA();
a.methodA();
}
}
在这个例子中,我们使用import com.example.package1.ClassA
语句将ClassA
引入进来,并在main
方法中创建了一个ClassA
对象并调用了其中的methodA
方法。由于methodA
方法是公共的(public),因此可以在其他包中的类中调用它。
而如果没有使用public关键字修饰,则只能在同一个包中的其他类中被访问和调用,无法跨越包边界。如果没有修饰符将默认为default“包级私有”,即使跨包导入也无法访问。因为没有public的话直接导入不了
总的来说,public关键字是Java中非常重要的关键字之一,它使得程序员可以更加方便地进行类的封装、继承、多态等面向对象编程的特性,也使得Java的代码重用性更加高效。
三、private:类和该类实例共同所有
1、只有private修饰 (偏向实例所有),只能在当前类中访问通过实例或非static方法访问。
private关键字用于指定类和实例共同所有的访问级别,表示该变量或方法只能在当前类中访问
public class MyClass {
private int myVariable;
private void myMethod() {
// do something
}
}
这里,myVariable变量和myMethod()方法都被指定为private,所以只有MyClass类内部可以访问它们。
package com.example.mypackage;
public class Address {
//成员变量中的实例变量
//城市
public String city;
//街道
public static String street;
//邮编
private String zipcode;
public void fun(){
zipcode = "jzq666";
System.out.println(zipcode);
}
public static void main(String[] args) {
Address a = new Address();
a.fun(); //jzq666
a.zipcode = "jzq";
System.out.println(a.zipcode); //jzq
}
}
2、private static共同修饰 (偏向类所有)只能在当前类中访问通过类或static方法访问。
只能在当前类中访问通过类或static方法访问。出了类就不能访问了,得通过接口
四、protected:表示受保护的,只能在当前类、同一包中的其他类或该类的子类中访问。
protected关键字用于指定受保护的访问级别,表示该变量或方法只能在当前类、同一包中的其他类或该类的子类中访问
public class MyClass {
protected int myVariable;
protected void myMethod() {
// do something
}
}
这里,myVariable变量和myMethod()方法都被指定为protected,所以除了MyClass类内部,同一包中的其他类和MyClass的子类也可以访问它们。
五、static:纯类级私有,表示静态的,用于指定类变量或类方法。
一般情况下没有static和final为纯实例私有
static关键字用于指定类变量或类方法,表示它们属于类而不是属于实例对象,可以在没有创建实例对象的情况下直接使用。
一般通过类来访问,但是 变量(属性)和方法 可以通过实例来强制访问,其实实例并没用到,相当于还是类来访问
public class MyClass {
public static int myStaticVariable;
public static void myStaticMethod() {
// do something
}
}
这里,myStaticVariable变量和myStaticMethod()方法都被指定为static,所以可以通过类名直接访问它们,而不需要创建实例对象。
六、final:表示不可变的,用于指定一个类、方法或变量不可被修改。
final关键字用于指定不可变的特性,可以用于指定一个类、方法或变量不可被修改
1、final是一个关键字,表示最终的,不可变的
2、final修饰的类无法继承
3、final修饰的方法无法覆盖(重写)
4、fina修饰的变量一旦赋值之后,不可重新赋值,即final修饰的变量只能赋一次值
- final修饰的成员变量
- 必须要进行初始化赋值,不能使用默认初始化,因为成员会进行初始化赋默认值,final修饰赋值后不可修改,所以需要再初始化赋值
- final修饰的实例变量,系统不负责赋默认值,要求程序员必须手动赋值
这个手动赋值,在变量后面赋值可以,在构造方法中赋值也可以。- final修饰的局部变量
- 没有默认初始化,不算赋值,需手动赋值
5、final修饰的引用,一日指向某个对象之后,不能再指向其它对象,那么被指向的对象无法被均圾回收器回收,直到当前方法结束,才会释放内存
final修饰的引用虽然指向某个对象之后不能指向其它对象,但是所指向的对象内部的内存是可以被修改的。
6、final修饰的实例变量,一般和static联合使用,被称为常量,又称静态常量。
- 常量的定义语法格式: public static final 数据类型 常量名 = 值;
- 常量和静态变量都是存储在方法区,都是在类加载时初始化,调用方法也一样,区别在于常量不能变
- 常量名全部大写,每个单词之间采用下划线衔接
- 常量一般都是公共的,采用public修饰
7、如果一个final变量既在声明时进行了初始化,又在构造函数中进行了初始化,那么在构造函数中的初始化赋值会覆盖声明时的初始化值。
public class MyClass {
public final int myFinalVariable = 100;
public final void myFinalMethod() {
// do something
}
}
这里,myFinalVariable变量和myFinalMethod()方法都被指定为final,所以它们不能被修改。
FinalTest01.java
package com.final关键字.javase;
/**
* 关于java语言中的final关键字:
* 1、final是一个关键字,表示最终的,不可变的
* 2、final修饰的类无法继承
* 3、final修饰的方法无法覆盖
* 4、final修饰的变量一旦赋值之后,不可重新赋值
*
*/
public class FinalTest01 {
public static void main(String[] args) {
String s = "123";
System.out.println(s.length());
String str = new String("666");
System.out.println(str);
String s1 = "abc";
System.out.println(s1.replaceAll("c","a"));
System.out.println(s1.trim());
System.out.println(s1.isEmpty());
}
}
FinalTest02.java
package com.final关键字.javase;
public class FinalTest02 {
//第一种解决方案
final int i = 10;
//第二种解决方案
final int j;
public FinalTest02(){
this.j = 10;
}
public static void main(String[] args) {
final int k;
k = 10;
}
}
FinalTest03.java
package com.final关键字.javase;
public class FinalTest03 {
public static void main(String[] args) {
User u = new User(100);
//引用重新赋值
// u = new User(200);
System.out.println(u.getId());
final User u1 = new User(300);
//final修饰的引用,一旦指向某个对象之后,不能在指向其他对象,那么被指向的对象无法被垃圾回收机制回收
//u1 = new User(400);
System.out.println(u1.getId());
//final修饰的引用虽然指向某个对象之后不能再指向其他对象,但是所指向的对象内部的内存是可以被修改的
u1.setId(400);
System.out.println(u1.getId());
}
}
class User {
private int id;
public User() {
}
public User(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
FinalTest04.java
package com.final关键字.javase;
public class FinalTest04 {
public static void main(String[] args) {
System.out.println(Chinese.COUNTRY);
System.out.println("圆周率:" + Math.PI);
}
}
class Math{
public static final double PI = 3.1415926;
}
class Chinese{
//需求:每一个中国人的国籍都是中国,而且国籍不会改变
//常量
static final String COUNTRY = "中国";
}
FinalTest01.java
public class FinalTest01 {
public static void main(String[] args){
A a = new B();
if(a instanceof B){
B b = (B)a;
b.doSome();
}
final int i;
//只能赋一次值
i = 100;
//i = 200;
System.out.println(i);
}
}
class A extends Object{
}
class B extends A {
public void doSome(){
System.out.println("A 's doSome!");
}
}
FinalTest02.java
public class FinalTest02{
public static void main(String[] args){
//Person p = new Person(30);
//System.out.println(p.age);
//======================================
final Person p1 = new Person(30);
//错误: 无法为最终变量p1分配值
//p1 = new Person(30);
//错误: 无法为最终变量p1分配值
//p1 = null;
System.out.println(p1.age);
p1.age = 50;
System.out.println(p1.age);
}
}
class Person extends Object{
int age;
public Person(){
super();
}
public Person(int age){
super();
this.age = age;
}
}
FinalTest03.java
public class FinalTest03{
public static void main(String[] args){
User u = new User(80);
//System.out.println(u.age);
}
}
class User extends Object{
//成员变量
//错误: 变量 age 未在默认构造器中初始化
//final int age;
final int age = 20;
final double height = 180;
//要在系统赋默认值之前赋值
final double weight;
public User(double weight){
//这也算赋值
this.weight = weight;
}
}
FinalTest04.java
public class FinalTest04{
public static void main(String[] args){
Chinese c = new Chinese();
System.out.println(Chinese.COUNTRY);
}
}
class Chinese extends Object{
//实例变量
String idCard;
//实例变量
String name;
//静态常量,一般都是公开的,常量名大写,单词间用下划线_ 隔开
public static final String COUNTRY = "中国";
}
七、abstract:表示抽象的,用于指定抽象类和抽象方法。
abstract关键字用于指定抽象类和抽象方法,表示这些类和方法没有具体实现,需要由子类实现
public abstract class MyAbstractClass {
public abstract void myAbstractMethod();
}
这里,MyAbstractClass类和myAbstractMethod()方法都被指定为abstract,所以需要由子类实现myAbstractMethod()方法。
八、synchronized:表示同步的,用于保证多线程并发执行时的安全性。
synchronized关键字用于保证多线程并发执行时的安全性,可以用于指定方法或代码块
public class MyClass {
public synchronized void myMethod() {
// do something
}
}
这里,myMethod()方法被指定为synchronized,表示在执行该方法时会对该方法加锁,保证同一时间只有一个线程可以访问该方法
九、volatile:表示易变的,用于指定变量在多线程环境下的可见性和原子性。
volatile关键字用于指定变量在多线程环境下的可见性和原子性
public class MyClass {
public volatile int myVariable;
}
这里,myVariable变量被指定为volatile,表示该变量在多线程环境下的读写操作是原子的,且在一个线程中修改该变量后,其他线程会立即看到该变量的修改结果。
十、transient:表示瞬态的,用于指定变量不参与序列化过程。
transient关键字用于指定变量不参与序列化过程
public class MyClass implements Serializable {
private transient int myTransientVariable;
}
这里,myTransientVariable变量被指定为transient,表示在将MyClass对象序列化时,该变量不会被序列化。
十一、native:表示本地的,用于指定方法用本地语言实现。
native关键字用于指定方法用本地语言实现
public class MyClass {
public native void myNativeMethod();
}
这里,myNativeMethod()方法被指定为native,表示该方法的实现是用本地语言(如C、C++等)实现的。
System.out.println() 输出原理
在Java虚拟机启动时,会执行一些初始化操作,其中包括System
类的初始化。在System
类初始化期间,会创建一个PrintStream
对象,并将它赋值给System.out
。这个PrintStream
对象代表了标准输出流,并且默认情况下将其输出到控制台上。
System.out
对象的初始化是由System
类的静态代码块完成的,这个代码块会在System
类被加载时执行。System
类的加载通常在Java虚拟机启动时就发生了。当Java虚拟机首次加载System
类时,它会执行System
类的静态代码块,并将System.out
初始化为一个PrintStream
对象。
需要注意的是,虽然System.out
的初始化是在Java虚拟机启动时发生的,但如果在程序运行时重新赋值System.out
,则可以改变输出流的目标。例如,你可以将System.out
重新赋值为一个文件输出流,这样所有的System.out
输出都会被写入该文件。
这些修饰符关键字可以单独使用,也可以组合使用。组合使用时,一般的顺序为public、protected、private、static、abstract、final、synchronized、volatile、transient和native。
十二、访问控制权限修饰符
1、访问控制权限修饰符来控制元素的访问范围
2、访问控制权限修饰符包括:
- public:表示公开的,在任何位置都可以访问
- protected:本类,同包,子类 可访问
- 缺省:本类,同包 可访问
- private:表示私有的,只能在本类中访问
3、访问控制权限修饰符可以修饰类、接口,成员变量、方法
属性:4个都能用
实例 / 静态方法:4个都能用
类:public和默认能用,其他不行
接口:public和默认能用,其他不行
4、当某个数据希望某个子类使用,使用protected进行修饰
5、修饰符的范围:
private < 缺省 < protected < public6、类只能采用public和缺省 的修饰符修饰(内部类除外)
Test01.java
package com.访问控制权限修饰符.javase;
public class Test01 {
public static void main(String[] args) {
User u = new User();
//protected 和 缺省 修饰符修饰的变量,方法 和类都可以同包中调用
System.out.println(u.i);
System.out.println(u.j);
}
}
User.java
package com.访问控制权限修饰符.javase;
public class User {
//受保护的
protected int i = 10;
//缺省的
int j = 20;
//可以修饰方法
private void m1(){
}
void m2(){
}
protected void m3(){
}
public void m4(){
}
public static void sumInt(int num){
int sum = 0;
boolean flag = true;
for (int i = 2 ; i <= num ; i++){
for(int j = 2 ; j < i ; j++){
if(i % j == 0){
flag = false;
break;
}
flag = true;
}
if(flag){
sum += i;
}
}
System.out.println(sum);
}
public static void main(String[] args) {
sumInt(100);
}
}
Driver.java
package com.访问权限控制符001.javase;
import com.访问控制权限修饰符.javase.User;
public class Driver extends User {
public void m(){
System.out.println(this.i);
//j为缺省修饰符,只能在同一个包中调用
//System.out.println(this.j);
}
}
UserTest.java
package com.访问权限控制符001.javase;
import com.访问控制权限修饰符.javase.User;
public class UserTest {
public static void main(String[] args) {
User u = new User();
//i 为 protected 修饰,只能在同一个包中 或者 不同包中的该类的子类中调用
//System.out.println(u.i);
}
}
第十二章、类
一、概述
java中一切皆对象,一切皆类型,只要是属于该类型的变量(引用),都可以调该类型中所有的属性和方法(包括实例和静态的)
二、类的定义
语法结构
[修饰符列表] class 类名 { //修饰符列表可选
属性;
方法;
}
注意:Java语言中所有的class都属于引用数据类型,class后跟的“类名”为引用数据类型,因为通过class定义的是类,属于引用数据类型。
三、类级别和实例级别访问原则
实例级别 < 类级别
- 访问方面
- 小级别可以直接访问大级别的,大级别不能直接访问小级别的
- 包含方面
- 小级别中有大级别的信息,大级别中没有小级别的信息
- 实例级别中有类级别的信息
- 类级别中没有实例级别的信息
注意:类中的一切属性和方法,在类内类外都可以通过实例对象或this访问和修改
四、实例成员永远在运行时解析,静态成员永远在编译时解析
- 当操作实例成员时,起决定性作用的是引用中保存的值,是在运行时确定
- 当操作静态成员时,起决定性作用的是引用的类型,是在编译时确定
五、不支持动态添加属性和方法
Java 是一种静态类型语言,不像 Python 那样支持在运行时动态添加属性和方法。在 Java 中,类的成员(属性和方法)必须在类定义时进行显式声明,并且在运行时无法添加新的成员。
在 Java 中,要添加新的属性或方法,您需要在类定义中进行声明,然后在相应的对象上调用它们。例如:
public class MyClass {
// 属性声明
private int age;
// 方法声明
public void sayHello() {
System.out.println("Hello, I am a method!");
}
// Getter 和 Setter 方法用于访问和修改属性
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后在其他类中使用 MyClass
的实例时,可以访问已经声明的属性和方法:
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.sayHello(); // 输出: Hello, I am a method!
obj.setAge(25);
System.out.println("Age: " + obj.getAge()); // 输出: Age: 25
}
}
在 Java 中,要动态地添加属性和方法,您需要重新编译类并重新运行程序。这与 Python 的动态性不同,在 Python 中,您可以在运行时动态地修改类和实例的属性和方法。因此,Java 在这方面相对更为严格。
六、属性
属性(包括实例和静态)都是是采用一个变量的形式来完成定义的,为成员变量,在类体中定义,没有赋值时采用默认值;
- 只有变量能保存数据
- 注意:引用数据