类:

类是封装对象的行为和属性的载体,具有相同属行和行为的一类实体。类中包含方法和属性。比如一个Dog类 ,

public class Dog{
	// dog具有什么特征呢? 比如颜色,大小,种类 他这些特征用java表示称为属性。
    private String color;
    private String age;
    private String category;
    // dog具有什么行为呢? 比如 叫,跑,喝水,
    public  void run(){
     System.out.println("狗仔跑");
    } 
    
    public void drink(){
     System.out.println("狗喝水")
    }
}

类中的构造方法:

1.构造方法没有返回值

2.名称与类名相同,在构造方法中可以为成员变量赋值,也就是初始化成员变量,若在类中的构造方法都不是无惨的构造方法,编译器不会为类设置一个无参的构造方法,在类中没有设置构造方法时编译器才会在类中自定义一个无参的构造方法;

public class Dog{
	// dog具有什么特征呢? 比如颜色,大小,种类 他这些特征用java表示称为属性。
    private String color;
    private String age;
    private String category;
    // 定义无参构造方法
    public Dog(){
    
    }
    // 定义有参构造方法
    public Dog( String color,String age){
    	// this就是指的当前对象
    	this.age=age;
        this.color=color;
    }
    
    // dog具有什么行为呢? 比如 叫,跑,喝水,
    public  void run(){
     System.out.println("狗仔跑");
    } 
    
    public void drink(){
     System.out.println("狗喝水")
    }
}

类中的变量

Java程序的变量大体可分为成员变量和局部变量。其中局部变量可分为如下3类。

■ 形参:在方法签名中定义的局部变量,由方法调用者负责为其赋值,随方法的结束而消亡。

■方法内的局部变量:在方法内定义的局部变量,必须在方法内对其进行显式初始化。这种类型的局部变量从初始化完成后开始生效,随方法的结束而消亡。

■代码块内的局部变量:在代码块内定义的局部变量,必须在代码块内对其进行显式初始化。这种类型的局部变量从初始化完成后开始生效,随代码块的结束而消亡。

类中的变量分为实例变量和静态变量。

类体内定义的变量被称为成员变量(英文是Field)。如果定义该成员变量时没有使用static修饰,该成员变量又被称为非静态变量或实例变量;如果使用了static修饰,则该成员变量又可被称为静态变量或类变量。

实例变量

由于同一个JVM内每个类只对应一个Class对象,因此同一个JVM内的一个类的类变量只需一块内存空间;但对于实例变量而言,该类每创建一次实例,就需要为实例变量分配一块内存空间。也就是说,程序中有几个实例,实例变量就需要几块内存空间。

定义实例变量时指定的初始值、初始化块中为实例变量指定的初始值、构造器中为实例变量指定的初始值,三者的作用完全类似,都用于对实例变量指定初始值。经过编译器处理之后,它们对应的赋值语句都被合并到构造器中。在合并过程中,定义变量语句转换得到的赋值语句、初始化块里的语句转换得到的赋值语句,总是位于构造器的所有语句之前;合并后,两种赋值语句的顺序保持它们在源代码中的顺序

类变量:

类变量,是各个实例所共享的,当此类第一次初始化时就会在内存中开启一块不变的内存存各个变量的值,然后待其他实例对象所要调用时直接可以调用

从语法角度来看,程序可以在2个地方对类变量执行初始化:

■ 定义类变量时指定初始值;

■ 静态初始化块中对类变量指定初始值。这两种方式的执行顺序与它们在源程序中排列顺序相同

类变量的初始化时机总是处于实例变量的初始化时机之前。

类中的成员方法:

成员方法对应于类对象的行为,成员方法可以是有参也可以是无参,可以有返回值也可以没有返回值,成员方法中可以调用其他成员方法也可以调用类成员变量,成员方法中也可以定义成员变量这是的成原变量为局部变量,

静态成员变量,静态成员方法:都用 类名.成员方法名。类名.成员变量掉用。静态方法中不可以用this关键字,静态方法不能直接调用非静态方法静态类中不能用this传值,可以用 return进行返回值,和直接输出;用来测试静态方法

1.静态方法不可以访问非静态变量

2.非静态方法可以访问静态方法;

静态方法或属性在类加载时产生的。非静态方法是在new中产生的调用静态方法的格式:类名.静态方法名(参数);

This关键字:

this也可以调用成员方法和成员变量只是不太规范。主要用于当前对象的调用;This也可以做为方法的返回值

this可以在当前方法中获取当前对象的引用。

当this在构造器中时,this代表正在初始化的Java对象

Static关键字:

new创建对象,数据存储空间才会分配,其方法才会对外界调用。而static修饰的:

(1)如果没有创建对象,也可以进行调用这个方法。

(2)只想为某特定的区域分配单一存储空间,而不考虑究竟创建出多少对象。

当一个事物声明static那就意味着这个域或方法不会与包含他那个类所关联在一起,所以即使从未创建某个类的任何对象,也可以调用其static方法,或访问static域。

比如:

class StaticTest{

	static int i= 47;

	String a =1;

}

StaticTest  st1=new  StaticTest();

StaticTest  st22=new  StaticTest();

st1.i和st22.i指向的是同一个存储空间,他们具有相同的值。
// 类变量,是各个实例所共享的,当此类第一次初始化时就会在内存中开启一块不变的内存存各个变量的值,然后待其他实例对象所要调用时直接可以调用

java中类里面可以包含类吗 java类可以没有属性吗_对象

尽管当static作用于某个字段时,肯定会改变数据创建的方式(因为一个static字段对每个类来说都只有一份存储空间,而非static字段则是对每个对象有一存储空间)

static方法的内部能调用非静态方法。

static加载顺序:

父类静态代码块->子类静态代码块-->父类非静态代码块-->父类构造方法-->子类非静态代码块-->子类构造方法。

Super关键字:

子类可易继承父类的非私有变量,和方法作为自己的成员变量和成员方法,子类中的方法名与父类的方法名相同时称为子类隐藏了父类的成员变量,子类不能继承父类的成员方法,此时称为重写父类的成员变量;

若子类想访问父类中被子类隐藏的成员方法或变量时可以用super关键字 此时必须由子类来使用super关键字;用途:

1.super(参数列表);调用父类有参的构造器

2.操作被隐藏在父类中的成员变量或被重写的方法格式:super.成员变量名 必须放在方法的第一个语句中;

Super.成员方法名([参数列表])Extends 子类继承父类,子接口继承父接口,implements子类对接口的实现

final关键字:

final作用于数据,方法,类。

1.一个永不改变的编译时常量。

2.一个在运行时被初始化的值,而你不希望它被改变。

一个既是static又是final的域只占据一段不能改变的存储空间。

空白的final: java允许生成一个空白的final;所谓的空白的final是指的被声明为final但又未给定初始值的域。比如: private final int j;

final参数:java允许声明一个参数为final的对象。

void(final Gizmo g){
g=new Gizmo() // Illegal ---g is final
}

final修饰实例变量:被final修饰的实例变量必须显式指定初始值,而且只能在如下3个位置指定初始值。

■ 定义final实例变量时指定初始值;

■ 在非静态初始化块中为final实例变量指定初始值;

■ 在构造器中为final实例变量指定初始值。

final实例变量必须显式地被赋初始值,而且本质上final实例变量只能在构造器中被赋初始值。当然,就程序员编程来说,还可在定义final实例变量时指定初始值,也可以初始化块中为final实例变量指定初始值,但它们本质上是一样的。除此之外,final实例变量将不能被再次赋值。

final修饰类变量: 对于final类变量而言,同样必须显式指定初始值,而且final类变量只能在2个地方指定初始值:

■ 定义final类变量时指定初始值;

■ 在静态初始化块中为final类变量指定初始值。

对一个final变量,不管它是类变量、实例变量,还是局部变量,只要定义该变量时使用了final修饰符修饰,并在定义该final类变量时指定了初始值,而且该初始值可以在编译时就被确定下来,那么这个final变量本质上已经不再是变量,而是相当于一个直接量

final方法: 定义为final方法的原因有两个;1.把方法锁定,以防止任何继承类修改它的含义(保持不变,防止覆盖)。2.是效率

private和final关键字: 类中所有的private方法都隐式的指定为final,由于无法取用private方法,所以也就无法进行覆盖他,对于private方法添加final关键字并不能给方法增加任何意义。

final类: 将类设置为final即使永远不需要做任何变动,或者出于安全不希望有子类。由于final类禁止继承,所以final类的方法都隐式的指定为final的,因为无法覆盖他们,同样也可以给final类的方法添加final修饰词,但是这没有任何意义。

final修饰引用类型的时候,引用的指向不能修改,但是引用的值可以改

final Student stu = new Student(1,"assd");
stu.setAge(12); // 这里是可以的,因为改变的是堆内存中的数据,引用还是指的这一个堆内存地址。
stu=new Student(); // 这里是不允许的因为,final类型是不可以变的。

final好处:

1.final关键字提高了性能。JVM和Java应用都会缓存final变量。

2.final变量可以安全的在多线程的环境下共享,而不需要额外的同步开销。

3.使用final关键字,JVM会对方法,变量进行优化。

final修饰的String类

String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /**该值用于字符存储。  */
    private final char value[];

    /**  缓存字符串的哈希代码*/
    private int hash; // Default to 0

    /** 使用jdk1.0.2中的serialVersionUID实现互操作性*/
    private static final long serialVersionUID = -6849794470754667710L;

    /**
     * 类字符串在序列化流协议中是特殊大小写的。
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
    /**
     *初始化新创建的{@code String}对象,使其表示空字符序列。请注意,此构造函数的用法是没有必要,因为字符串是不可变的。
     */
    public String() {
        this.value = "".value;
    }
直接赋值方式创建对象是在方法区的常量池
String str="hello";//直接赋值的方式

通过构造方法创建字符串对象是在堆内存
String str=new String("hello");//实例化的方式

1)直接赋值(String str = "hello"):只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。

2)构造方法(String str=  new String("hello");):会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过public  String intern();方法进行手工入池。

在开发的过程中不会采用构造方法进行字符串的实例化。

Q&A String的不可变性

Strings are constant; their values cannot be changed after theyare created. String buffers support mutable strings.Because String objects are immutable they can be shared. 
    For example:
 String str = "abc";
 与下面的相等;
 *     char data[] = {'a', 'b', 'c'};
 *     String str = new String(data);
 * Here are some more examples of how strings can be used:
 * <blockquote><pre>
 *     System.out.println("abc");
 *     String cde = "cde";
 *     System.out.println("abc" + cde);
 *     String c = "abc".substring(2,3);
 *     String d = cde.substring(1, 2);
 * </pre></blockquote>
 * <p>

Q&A 为何String设计为不可变?

1、运行时常量池的需要,节省内存空间。

  比如执行 String s = "abc";执行上述代码时,JVM首先在运行时常量池中查看是否存在String对象“abc”,如果已存在该对象,则不用创建新的String对象“abc”,而是将引用s直接指向运行时常量池中已存在的String对象“abc”;如果不存在该对象,则先在运行时常量池中创建一个新的String对象“abc”,然后将引用s指向运行时常量池中创建的新String对象。这样在运行时常量池中只会创建一个String对象"abc",这样就节省了内存空间。

2、同步

  因为String对象是不可变的,所以是多线程安全的,同一个String实例可以被多个线程共享。这样就不用因为线程安全问题而使用同步。

3、允许String对象缓存hashcode

  查看上文JDK1.8中String类源码,可以发现其中有一个字段hash,String类的不可变性保证了hashcode的唯一性,所以可以用hash字段对String对象的hashcode进行缓存,就不需要每次重新计算hashcode。所以Java中String对象经常被用来作为HashMap等容器的键。

4、安全性

  如果String对象是可变的,那么会引起很严重的安全问题。比如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为String对象是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变String引用指向的对象的值,造成安全漏洞。

Q&A String s = new String("xyz");创建了几个String Object?二者之间有什么区别?

两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新String对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。

构造器

考虑到在初始化期间要自动代用构造器,构造器采用与类名相同的名称,在java中“初始化”和“创建”捆绑在一起,两者不能分离。

构造器只是负责对Java对象实例变量执行初始化(也就是赋初始值),在执行构造器代码之前,该对象所占的内存已经被分配下来,这些内存里值都默认是空值—对于基本类型的变量,默认的空值就是0或false;对于引用类型的变量,默认的空值就是null

  • 是一个类创建对象的根本途径,如果一个类没有构造器,这个类通常无法创建实例。因此, Java语言提供了一个功能:如果程序员没有为一个类编写构造器,则系统会为该类提供一个默认的构造器静态变量调用:
  • Java编程时不要使用对象去调用static修饰的Field、方法,而是应该使用类去调用static修饰的Field、方法!如果在其他Java代码中看到对象调用static修饰的Field、方法的情形,完全可以把这种用法当成假象,将其替换成用类来调用static修饰的Field、方法的代码。

对象:

对象是通过new关键字来创建的,通过引用来接收对象,当对象创建出来后引用就会为对象分配内存,new字是创建对象的操作符,对象的比较有两种形式:

1.“==”运算符是用来比较两个对象引用的地址是否相等,

2.“equal()”方法是来比较两对象引用的内容是否相等。对象的销毁是引用结束后就会被垃圾处理器进行回收;

Q&A 创建对象的方法:

1.使用new关键字。

2.使用Class类中的newInstance方法,newInstance方法调用无参构造方器创建对象。Class.forName.newInstance;

3.使用clone方法、

4.反序列化。

Q&A 对象的产生过程以及存储:

对象的产生:

new将对象存储在堆中,所以用new创建一个对象---特别小的,简单的变量,往往不是很有效。因此对于(基本类型)java不用new来创建这样的变量,而是创建一个并非是引用的“自动”变量。这个变量的值直接存储"值"到堆栈中。

java中类里面可以包含类吗 java类可以没有属性吗_实例变量_02

程序创建,运行时对象是如何分配呢?内存是怎样分配呢?

java中类里面可以包含类吗 java类可以没有属性吗_java中类里面可以包含类吗_03

对象产生的时机 类加载,然后进行对象的实例化:

Q&A 什么时候会进行类加载?

1.创建类的实例,也就是new一个对象

2.访问某个类或接口的静态变量,或者对该静态变量赋值

3.调用类的静态方法

4.反射(Class.forName("A"))

5.初始化一个类的子类(会首先初始化子类的父类)

6.JVM启动时标明的启动类,即文件名和类名相同的那个类

new ObjectInitTest() 对象的产生过程 1.JVM会在ObjectInitTest.class文件 2.先加载类级别的成员(静态成员变量 静态块初始化) 3.再加载对象级别的成员(实例成员变量 实例块初始化)

Q&A 什么时候进行对象的实例化?

类加载成功(或已加载过)后,就开始进行对象的实例化了。对象的实例化依次进行了如下几个步骤:

1.对象在堆中的内存空间分配

2.初始化零值,这时会将实例变量都赋予零值

3.设置对象头,对象头保存了一些对象在运行期的状态信息,包括类信息地址(知道对象是属于哪个类的)、hashcode(用于hashmap和hashset等hash结构中)、GC分代年龄(可以依此确定对象何时该放入老年代)等

4.init方法执行,这时对变量的实例变量进行初始化

对象初始化的过程也是线程安全的动作。

Q&A  StringBuffer StringBulider String 的区别:

StringBuffer线程安全,StringBulider线程不安全,底层实现StringBuffer比StringBulider多一个Synchronized.从源码中可以看得到:

StringBuffer源码分析:
@Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized int capacity() {
        return value.length;
    }


    @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > value.length) {
            expandCapacity(minimumCapacity);
        }
    }

    /**
     * @since      1.5
     */
    @Override
    public synchronized void trimToSize() {
        super.trimToSize();
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized void setLength(int newLength) {
        toStringCache = null;
        super.setLength(newLength);
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized char charAt(int index) {
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        return value[index];
    }

String 是否可以继承,“+”如何实现的,与StringBuffer区别?

java中通过使用“+”符号串联时实际是使用的StringBuilder实例的appdenf()方法来实现的。

 

注:本文章由kay三石在2021年1月27日重写修改了。