標籤: iphone維修

  • 電動巴士開進太魯閣,花縣繼續推動綠能公車政策

    電動巴士開進太魯閣,花縣繼續推動綠能公車政策

    花蓮縣太魯閣客運公司今年繼續辦理增資,以採購新的電動巴士。自9月起,往來於花蓮市區、太魯閣、東華大學等地的公車路線,將有電動巴士陸續開始運行。

    花蓮縣政府的「綠能公車」政策於2014年核定太魯閣客運公司的301線、302線兩條公車路線上路,且須採用電動巴士運行。一開始因電動巴士品質不夠穩定,一度改租用柴油巴士,但受到反彈而停駛;之後在地方民代的協調下,301、302線規劃復駛,並透過增資採購7輛低底盤K9電動巴士車輛,重新展開電動巴士服務。

    太魯閣客運公司經理表示,本次採購的7輛K9電動巴士為台灣凱勝綠能科技所生產,車體採用全鋁合金打造,並配有緊急自動滅火系統、ABS防鎖剎車等。車輛搭載的電池為台灣長利科技的磷酸鋰鐵電池,每次充飽電的續航力可達250公里以上。

    K9巴士的性能佳,目前在苗栗客運、捷順交通、雲林客運、南台灣客運等公司都有同款車輛正在提供服務。太魯閣客運採購K9巴士時,為挑戰巴士的續航力,還安排一輛K9巴士直接從苗栗車體廠繞過北台灣、蘇花公路一路行駛到花蓮東華大學,總里程320.7公里,全程開冷氣;駛達目的地後,電池的電量還有43.9%之多。

    目前,301線已配合東華大學開學而正式復駛。太魯閣客運希望新城到天祥的302線也能在年底前復駛,預計將有6輛電動巴士投入服役。

    (照片來源:東華大學/太魯閣客運)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司“嚨底家”!

    ※推薦評價好的iphone維修中心

    聚甘新

  • spring源碼分析——BeanPostProcessor接口

    spring源碼分析——BeanPostProcessor接口

     

      BeanPostProcessor是處理bean的後置接口,beanDefinitionMaps中的BeanDefinition實例化完成后,完成populateBean,屬性設置,完成

    初始化后,這個接口支持對bean做自定義的操作。

    一:BeanPostProcessor的使用

    定義一個測試用的model對象,name屬性默認為hello

    public class BeanDemo {
    
    	private String name = "hello";
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	@Override
    	public String toString() {
    		final StringBuffer sb = new StringBuffer("BeanDemo{");
    		sb.append("name='").append(name).append('\'');
    		sb.append('}');
    		return sb.toString();
    	}
    }
    

      

    自定義一個MyBeanPostProcessor類,實現BeanPostProcessor接口

    @Service
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		return null;
    	}
    
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		if(beanName.equals("beanDemo")){
    			BeanDemo beanDemo = (BeanDemo)bean;
    			beanDemo.setName("kitty");
    			return beanDemo;
    		}
    		return bean;
    	}
    }
    

      

     

     

    從運行結果看,spring中維護的beanName為beanDemo的對象,name屬性為ketty

     

     

    二:看看源碼怎麼實現的

    1:實例化並且註冊到beanPostProcessors集合中

     

     

     

     

    主要的實例化邏輯在這個接口,這個接口的作用就是把所有實現BeanPostProcessor接口的類實例化,然後註冊到 beanPostProcessors這個緩存中

     

     

    	public static void registerBeanPostProcessors(
    			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
    		// 獲取所有實現接口BeanPostProcessor的beanName
    		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
    		// Register BeanPostProcessorChecker that logs an info message when
    		// a bean is created during BeanPostProcessor instantiation, i.e. when
    		// a bean is not eligible for getting processed by all BeanPostProcessors.
    		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
    		// Separate between BeanPostProcessors that implement PriorityOrdered,
    		// Ordered, and the rest.
    		/**
    		 * 把實現PriorityOrdered 和 Ordered 和 其他的處理器分開
    		 */
    		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
    		List<String> orderedPostProcessorNames = new ArrayList<>();
    		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    		/**
    		 * 1:遍歷集合postProcessorNames
    		 * 2:判斷類型,如果是PriorityOrdered,則實例化對象放入priorityOrderedPostProcessors集合,
    		 * Ordered 則放入orderedPostProcessorNames集合,其他的放入nonOrderedPostProcessorNames集合
     		 */
    		for (String ppName : postProcessorNames) {
    			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    				priorityOrderedPostProcessors.add(pp);
    				if (pp instanceof MergedBeanDefinitionPostProcessor) {
    					internalPostProcessors.add(pp);
    				}
    			}
    			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    				orderedPostProcessorNames.add(ppName);
    			}
    			else {
    				nonOrderedPostProcessorNames.add(ppName);
    			}
    		}
    
    		// First, register the BeanPostProcessors that implement PriorityOrdered.
    		// 首先對priorityOrderedPostProcessors集合中實例對象排序,然後註冊,放入beanFactory中緩存下來
    		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
    		// Next, register the BeanPostProcessors that implement Ordered.
    		// 然後再實例化實現Ordered接口的對象,完成註冊
    		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
    		for (String ppName : orderedPostProcessorNames) {
    			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    			orderedPostProcessors.add(pp);
    			if (pp instanceof MergedBeanDefinitionPostProcessor) {
    				internalPostProcessors.add(pp);
    			}
    		}
    		sortPostProcessors(orderedPostProcessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    
    		// Now, register all regular BeanPostProcessors.
    		// 最後實例化什麼都沒有實現的,完成實例化並註冊
    		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    		for (String ppName : nonOrderedPostProcessorNames) {
    			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    			nonOrderedPostProcessors.add(pp);
    			if (pp instanceof MergedBeanDefinitionPostProcessor) {
    				internalPostProcessors.add(pp);
    			}
    		}
    		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    
    		// Finally, re-register all internal BeanPostProcessors.
    		// 最後再次註冊內部postProcessor
    		sortPostProcessors(internalPostProcessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
    		// Re-register post-processor for detecting inner beans as ApplicationListeners,
    		// moving it to the end of the processor chain (for picking up proxies etc).
    		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    	}
    

      

     

     

    定義四類容器,高優先級有序、有序、無序、內部

     

     分類放入四種容器:

     

     

    註冊BeanPostProcessor,將實現BeanPostProcessor接口的對象放入beanPostProcessors緩存中

     

     

     

     

     

     

    註冊完PriorityOrdered的實現類后,再處理Ordered的實現類

     

     

    註冊什麼都沒有實現的BeanPostProcessor接口實現類,

     

     

    最後註冊內部的BeanPostProcessor對象

     

     到這裏BeanPostProcessor的實例化以及註冊工作完成,在beanFactory的beanPostProcessors集合中已經緩存了所有的beanPostProcessor的對象

     

    2:BeanPostProcessor的使用

    因為這個接口是bean的後置接口,所以需要bean創建並初始化完成,才可以發揮作用,上一步的緩存只是埋好點,以備使用,因為bean的實例化流程我們

    還沒有分析,這裏直接看一下怎麼使用的

     

     

    我們看一下init方法后的攔截,因為這個時候已經init完成,可以在後置接口中對bean做一下修改的操作

     

     

    調用到我們自定義的MyBeanPostProcessor實現類:

     

     

    把這個beanDemo對象屬性修改一下,修改完,再返回,將這個對象緩存到spring的一級緩存中。

     

     

    總結:

      BeanPostProcessor接口主要是對bean對象做一些自定義的操作,修改bean對象的信息,aop代理也是通過這種方式實現的,

    在refresh的registryBeanPostProcessor方法中實例化BeanPostProcessor對象,並且註冊到beanFactory容器的beanPostProcessors的緩存中,

    然後在後續的操作中攔截使用。

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司“嚨底家”!

    ※推薦評價好的iphone維修中心

    聚甘新

  • 菜渣開源一個基於 EMIT 的 AOP 庫(.NET Core)

    目錄

    • 1,快速入門
      • 1.1 繼承 ActionAttribute 特性
      • 1.2 標記代理類型
      • 2,如何創建代理類型
        • 2.1 通過API直接創建
    • 2,創建代理類型
      • 通過API
      • 通過 Microsoft.Extensions.DependencyInjection
        • 通過 Autofac
    • 3,深入使用
      • 代理類型
      • 方法、屬性代理
      • 上下文
        • 攔截方法或屬性的參數
      • 非侵入式代理

    Nuget 庫地址:https://www.nuget.org/packages/CZGL.AOP/

    Github 庫地址:https://github.com/whuanle/CZGL.AOP

    CZGL.AOP 是 基於 EMIT 編寫的 一個簡單輕量的AOP框架,支持非侵入式代理,支持.NET Core/ASP.NET Core,以及支持多種依賴注入框架。

    1,快速入門

    CZGL.AOP 使用比較簡單,你只需要使用 [Interceptor] 特性標記需要代理的類型,然後使用繼承 ActionAttribute 的特性標記要被代理的方法或屬性。

    1.1 繼承 ActionAttribute 特性

    ActionAttribute 是用於代理方法或屬性的特性標記,不能直接使用,需要繼承后重寫方法。

    示例如下:

        public class LogAttribute : ActionAttribute
        {
            public override void Before(AspectContext context)
            {
                Console.WriteLine("執行前");
            }
    
            public override object After(AspectContext context)
            {
                Console.WriteLine("執行后");
                if (context.IsMethod)
                    return context.MethodResult;
                else if (context.IsProperty)
                    return context.PropertyValue;
                return null;
            }
        }
    

    Before 會在被代理的方法執行前或被代理的屬性調用時生效,你可以通過 AspectContext 上下文,獲取、修改傳遞的參數。

    After 在方法執行后或屬性調用時生效,你可以通過上下文獲取、修改返回值。

    1.2 標記代理類型

    在被代理的類型中,使用 [Interceptor] 特性來標記,在需要代理的方法中,使用 繼承了 ActionAttribute 的特性來標記。

    此方法是侵入式的,需要在編譯前完成。

    [Interceptor]
    public class Test : ITest
    {
        [Log] public virtual string A { get; set; }
        [Log]
        public virtual void MyMethod()
        {
            Console.WriteLine("運行中");
        }
    }
    

    注意的是,一個方法或屬性只能設置一個攔截器。

    2,如何創建代理類型

    CZGL.AOP 有多種生成代理類型的方式,下面介紹簡單的方式。

    請預先創建如下代碼:

        public class LogAttribute : ActionAttribute
        {
            public override void Before(AspectContext context)
            {
                Console.WriteLine("執行前");
            }
    
            public override object After(AspectContext context)
            {
                Console.WriteLine("執行后");
                if (context.IsMethod)
                    return context.MethodResult;
                else if (context.IsProperty)
                    return context.PropertyValue;
                return null;
            }
        }
    
        public interface ITest
        {
            void MyMethod();
        }
    
        [Interceptor]
        public class Test : ITest
        {
            [Log] public virtual string A { get; set; }
            public Test()
            {
                Console.WriteLine("構造函數沒問題");
            }
            [Log]
            public virtual void MyMethod()
            {
                Console.WriteLine("運行中");
            }
        }
    

    2.1 通過API直接創建

    通過 CZGL.AOP 中的 AopInterceptor 類,你可以生成代理類型。

    示例如下:

                ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
                Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
                test1.MyMethod();
                test2.MyMethod();
    

    CreateProxyOfInterface 通過接口創建代理類型;CreateProxyOfClass 通過類創建代理類型;

    默認調用的是無參構造函數。

    2,創建代理類型

    通過API

    你可以參考源碼解決方案

    中的 ExampleConsole 項目。

    如果要直接使用 AopInterceptor.CreateProxyOfInterfaceAopInterceptor.CreateProxyOfClass 方法,通過接口或類來創建代理類型。

            ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
            Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
    

    如果要指定實例化的構造函數,可以這樣:

                // 指定構造函數
                test2 = AopInterceptor.CreateProxyOfClass<Test>("aaa", "bbb");
                test2.MyMethod();
    

    通過 Microsoft.Extensions.DependencyInjection

    Microsoft.Extensions.DependencyInjection 是 .NET Core/ASP.NET Core 默認的依賴注入容器。

    如果需要支持 ASP.NET Core 中使用 AOP,你可以在 Nuget 包中安裝 CZGL.AOP.MEDI

    如果你在控制台下使用 Microsoft.Extensions.DependencyInjection,你可以使用名為 BuildAopProxyIServiceCollection 拓展方法來為容器中的類型,生成代理類型。

    示例如下:

                IServiceCollection _services = new ServiceCollection();
                _services.AddTransient<ITest, Test>();
                var serviceProvider = _services.BuildAopProxy().BuildServiceProvider();
                serviceProvider.GetService<ITest>();
                return serviceProvider;
    

    你可以參考源碼解決方案中的 ExampleMEDI 項目。

    如果你要在 ASP.NET Core 中使用,你可以在 Startup 中,ConfigureServices 方法的最後一行代碼使用 services.BuildAopProxy();

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers();
                services.BuildAopProxy();
            }
    

    還可以在 ProgramIHostBuilder 中使用 .UseServiceProviderFactory(new AOPServiceProxviderFactory()) 來配置使用 CZGL.AOP。

    示例:

            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AOPServiceProxviderFactory())
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });
    

    可以參考解決方案中的 ExampleConsoleExampleWebMEDI 兩個項目。

    你不必擔心引入 CZGL.AOP 后,使用依賴注入會使程序變慢或者破壞容器中的原有屬性。CZGL.AOP 只會在創建容器時處理需要被代理的類型,不會影響容器中的服務,也不會幹擾到依賴注入的執行。

    通過 Autofac

    如果需要在 Autofac 中使用 AOP,則需要引用 CZGL.AOP.Autofac 包。

    如果你在控制台程序中使用 Autofac,則可以在 Build() 後面使用 BuildAopProxy()

                ContainerBuilder builder = new ContainerBuilder();
                builder.RegisterType<Test>().As<ITest>();
                var container = builder.Build().BuildAopProxy();
    
                using (ILifetimeScope scope = container.BeginLifetimeScope())
                {
                    // 獲取實例
                    ITest myService = scope.Resolve<ITest>();
                    myService.MyMethod();
                }
    
                Console.ReadKey();
            }
    

    要注意的是,在已經完成的組件註冊創建一個新的容器后,才能調用 BuildAopProxy() 方法,

    這樣針對一個新的容器你可以考慮是否需要對容器中的組件進行代理。

    如果在 ASP.NET Core 中使用 Autofac,你需要在 Program 類的 IHostBuilder 中使用:

    .UseServiceProviderFactory(new AutofacServiceProviderFactory())
    

    如果需要代理已經註冊的組件,則將其替換為:

     .UseServiceProviderFactory(new CZGL.AOP.Autofac.AOPServiceProxviderFactory())
    

    請參考 源碼解決方案中的 ExampleAutofacExampleWebAutofac 兩個項目。

    3,深入使用

    代理類型

    要被代理的類型,需要使用 [Interceptor]來標記,例如:

        [Interceptor]
        public class Test : ITest
        {
        }
    

    支持泛型類型。

    被代理的類型必須是可被繼承的。

    類型的構造函數沒有限制,你可以隨意編寫。

    在使用 API 創建代理類型並且實例化時,你可以指定使用哪個構造函數。

    例如:

    			string a="",b="",c="";
    			ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(a,b,c);
    

    API 會根據參數的多少以及參數的類型自動尋找合適的構造函數。

    方法、屬性代理

    為了代理方法或屬性,你需要繼承 ActionAttribute 特性,然後為方法或屬性標記此特性,並且將方法或屬性設置為 virtual

    一個類型中的不同方法,可以使用不同的攔截器。

            [Log1]
            public virtual void MyMethod1(){}
            
            [Log2]
            public virtual void MyMethod2(){}
    

    對於屬性,可以在屬性上直接使用特性,或者只在 get 或 set 構造器使用。

            [Log] public virtual string A { get; set; }
            
            // 或
            public virtual string A { [Log] get; set; }
            
            // 或
            public virtual string A { get; [Log] set; }
    

    如果在屬性上使用特性,相當於 [Log] get; [Log] set;

    上下文

    一個簡單的方法或屬性攔截標記是這樣的:

        public class LogAttribute : ActionAttribute
        {
            public override void Before(AspectContext context)
            {
                Console.WriteLine("執行前");
            }
    
            public override object After(AspectContext context)
            {
                Console.WriteLine("執行后");
                if (context.IsMethod)
                    return context.MethodResult;
                else if (context.IsProperty)
                    return context.PropertyValue;
                return null;
            }
        }
    
    

    AspectContext 的屬性說明如下:

    字段 說明
    Type 當前被代理類型生成的代理類型
    ConstructorParamters 類型被實例化時使用的構造函數的參數,如果構造函數沒有參數,則 MethodValues.Length = 0,而不是 MethodValues 為 null。
    IsProperty 當前攔截的是屬性
    PropertyInfo 當前被執行的屬性的信息,可為 null。
    PropertyValue 但調用的是屬性時,返回 get 的結果或 set 的 value 值。
    IsMethod 當前攔截的是方法
    MethodInfo 當前方法的信息
    MethodValues 方法被調用時傳遞的參數,如果此方法沒有參數,則 MethodValues.Length = 0,而不是 MethodValues 為 null
    MethodResult 方法執行返回的結果(如果有)

    攔截方法或屬性的參數

    通過上下文,你可以修改方法或屬性的參數以及攔截返回結果:

        public class LogAttribute : ActionAttribute
        {
            public override void Before(AspectContext context)
            {
                // 攔截並修改方法的參數
                for (int i = 0; i < context.MethodValues.Length; i++)
                {
                    context.MethodValues[i] = (int)context.MethodValues[i] + 1;
                }
                Console.WriteLine("執行前");
            }
    
            public override object After(AspectContext context)
            {
                Console.WriteLine("執行后");
    
                // 攔截方法的執行結果
                context.MethodResult = (int)context.MethodResult + 664;
    
                if (context.IsMethod)
                    return context.MethodResult;
                else if (context.IsProperty)
                    return context.PropertyValue;
                return null;
            }
        }
    
        [Interceptor]
        public class Test
        {
            [Log]
            public virtual int Sum(int a, int b)
            {
                Console.WriteLine("運行中");
                return a + b;
            }
        }
    
                Test test = AopInterceptor.CreateProxyOfClass<Test>();
    
                Console.WriteLine(test.Sum(1, 1));
    

    方法的參數支持 inrefout;支持泛型方法泛型屬性;支持異步方法;

    非侵入式代理

    此種方式不需要改動被代理的類型,你也可以代理程序集中的類型。

        public class LogAttribute : ActionAttribute
        {
            public override void Before(AspectContext context)
            {
                Console.WriteLine("執行前");
            }
    
            public override object After(AspectContext context)
            {
                Console.WriteLine("執行后");
                if (context.IsMethod)
                    return context.MethodResult;
                else if (context.IsProperty)
                    return context.PropertyValue;
                return null;
            }
        }
    
        public class TestNo
        {
            public virtual string A { get; set; }
            public virtual void MyMethod()
            {
                Console.WriteLine("運行中");
            }
        }
    
                TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(new ProxyTypeBuilder()
                    .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                    .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
    

    通過 ProxyTypeBuilder 來構建代理類型。

    代理方法或屬性都是使用 AddProxyMethod,第一個參數是要使用的攔截器,第二個參數是要攔截的方法。

    如果要攔截屬性,請分開設置屬性的 getset 構造。

    如果多個方法或屬性使用同一個攔截器,則可以這樣:

                TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
                    new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
                    .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                    .AddProxyMethod("LogAttribute", typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
    
                TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
                    new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
                    .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                    .AddProxyMethod(typeof(LogAttribute2), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
    

    在構造函數中傳遞過去所需要的攔截器,然後在攔截時使用。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司“嚨底家”!

    ※推薦評價好的iphone維修中心

    聚甘新

  • 【Flutter實戰】六大布局組件及半圓菜單案例

    【Flutter實戰】六大布局組件及半圓菜單案例

    老孟導讀:Flutter中布局組件有水平 / 垂直布局組件( RowColumn )、疊加布局組件( StackIndexedStack )、流式布局組件( Wrap )和 自定義布局組件(Flow)。

    水平、垂直布局組件

    Row 是將子組件以水平方式布局的組件, Column 是將子組件以垂直方式布局的組件。項目中 90% 的頁面布局都可以通過 Row 和 Column 來實現。

    將3個組件水平排列:

    Row(
      children: <Widget>[
        Container(
          height: 50,
          width: 100,
          color: Colors.red,
        ),
        Container(
          height: 50,
          width: 100,
          color: Colors.green,
        ),
        Container(
          height: 50,
          width: 100,
          color: Colors.blue,
        ),
      ],
    )
    

    將3個組件垂直排列:

    Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Container(
          height: 50,
          width: 100,
          color: Colors.red,
        ),
        Container(
          height: 50,
          width: 100,
          color: Colors.green,
        ),
        Container(
          height: 50,
          width: 100,
          color: Colors.blue,
        ),
      ],
    )
    

    在 Row 和 Column 中有一個非常重要的概念:主軸( MainAxis )交叉軸( CrossAxis ),主軸就是與組件布局方向一致的軸,交叉軸就是與主軸方向垂直的軸。

    具體到 Row 組件,主軸 是水平方向,交叉軸 是垂直方向。而 Column 與 Row 正好相反,主軸 是垂直方向,交叉軸 是水平方向。

    明白了 主軸 和 交叉軸 概念,我們來看下 mainAxisAlignment 屬性,此屬性表示主軸方向的對齊方式,默認值為 start,表示從組件的開始處布局,此處的開始位置和 textDirection 屬性有關,textDirection 表示文本的布局方向,其值包括 ltr(從左到右) 和 rtl(從右到左),當 textDirection = ltr 時,start 表示左側,當 textDirection = rtl 時,start 表示右側,

    Container(
      decoration: BoxDecoration(border: Border.all(color: Colors.black)),
      child: Row(
        children: <Widget>[
          Container(
            height: 50,
            width: 100,
            color: Colors.red,
          ),
          Container(
            height: 50,
            width: 100,
            color: Colors.green,
          ),
          Container(
            height: 50,
            width: 100,
            color: Colors.blue,
          ),
        ],
      ),
    )
    

    黑色邊框是Row控件的範圍,默認情況下Row鋪滿父組件。

    主軸對齊方式有6種,效果如下圖:

    spaceAround 和 spaceEvenly 區別是:

    • spaceAround :第一個子控件距開始位置和最後一個子控件距結尾位置是其他子控件間距的一半。
    • spaceEvenly : 所有間距一樣。

    和主軸對齊方式相對應的就是交叉軸對齊方式 crossAxisAlignment ,交叉軸對齊方式默認是居中。Row控件的高度是依賴子控件高度,因此子控件高都一樣時,Row的高和子控件高相同,此時是無法體現交叉軸對齊方式,修改3個顏色塊高分別為50,100,150,這樣Row的高是150,代碼如下:

    Container(
          decoration: BoxDecoration(border: Border.all(color: Colors.black)),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Container(
                height: 50,
                width: 100,
                color: Colors.red,
              ),
              Container(
                height: 100,
                width: 100,
                color: Colors.green,
              ),
              Container(
                height: 150,
                width: 100,
                color: Colors.blue,
              ),
            ],
          ),
        )
    

    主軸對齊方式效果如下圖:

    mainAxisSize 表示主軸尺寸,有 min 和 max 兩種方式,默認是 maxmin 表示盡可能小,max 表示盡可能大。

    Container(
    	decoration: BoxDecoration(border: Border.all(color: Colors.black)),
    	child: Row(
    		mainAxisSize: MainAxisSize.min,
    		...
    	)
    )
    

    看黑色邊框,正好包裹子組件,而 max 效果如下:

    textDirection 表示子組件主軸布局方向,值包括 ltr(從左到右) 和 rtl(從右到左)

    Container(
      decoration: BoxDecoration(border: Border.all(color: Colors.black)),
      child: Row(
        textDirection: TextDirection.rtl,
        children: <Widget>[
          ...
        ],
      ),
    )
    

    verticalDirection 表示子組件交叉軸布局方向:

    • up :從底部開始,並垂直堆疊到頂部,對齊方式的 start 在底部,end 在頂部。
    • down: 與 up 相反。
    Container(
      decoration: BoxDecoration(border: Border.all(color: Colors.black)),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        verticalDirection: VerticalDirection.up,
        children: <Widget>[
          Container(
            height: 50,
            width: 100,
            color: Colors.red,
          ),
          Container(
            height: 100,
            width: 100,
            color: Colors.green,
          ),
          Container(
            height: 150,
            width: 100,
            color: Colors.blue,
          ),
        ],
      ),
    )
    

    想一想這種效果完全可以通過對齊方式實現,那麼為什麼還要有 textDirectionverticalDirection 這兩個屬性,官方API文檔已經解釋了這個問題:

    This is also used to disambiguate start and end values (e.g. [MainAxisAlignment.start] or [CrossAxisAlignment.end]).

    用於消除 MainAxisAlignment.start 和 CrossAxisAlignment.end 值的歧義的。

    疊加布局組件

    疊加布局組件包含 StackIndexedStack,Stack 組件將子組件疊加显示,根據子組件的順利依次向上疊加,用法如下:

    Stack(
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Container(
          height: 170,
          width: 170,
          color: Colors.blue,
        ),
        Container(
          height: 140,
          width: 140,
          color: Colors.yellow,
        )
      ],
    )
    

    Stack 對未定位(不被 Positioned 包裹)子組件的大小由 fit 參數決定,默認值是 StackFit.loose ,表示子組件自己決定,StackFit.expand 表示盡可能的大,用法如下:

    Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Container(
          height: 170,
          width: 170,
          color: Colors.blue,
        ),
        Container(
          height: 140,
          width: 140,
          color: Colors.yellow,
        )
      ],
    )
    

    效果只有黃色(最後一個組件的顏色),並不是其他組件沒有繪製,而是另外兩個組件被黃色組件覆蓋。

    Stack 對未定位(不被 Positioned 包裹)子組件的對齊方式由 alignment 控制,默認左上角對齊,用法如下:

    Stack(
      alignment: AlignmentDirectional.center,
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Container(
          height: 170,
          width: 170,
          color: Colors.blue,
        ),
        Container(
          height: 140,
          width: 140,
          color: Colors.yellow,
        )
      ],
    )
    

    通過 Positioned 定位的子組件:

    Stack(
      alignment: AlignmentDirectional.center,
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Container(
          height: 170,
          width: 170,
          color: Colors.blue,
        ),
        Positioned(
          left: 30,
          right: 40,
          bottom: 50,
          top: 60,
          child: Container(
            color: Colors.yellow,
          ),
        )
      ],
    )
    

    topbottomleftright 四種定位屬性,分別表示距離上下左右的距離。

    如果子組件超過 Stack 邊界由 overflow 控制,默認是裁剪,下面設置總是显示的用法:

    Stack(
      overflow: Overflow.visible,
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Positioned(
          left: 100,
          top: 100,
          height: 150,
          width: 150,
          child: Container(
            color: Colors.green,
          ),
        )
      ],
    )
    

    IndexedStack 是 Stack 的子類,Stack 是將所有的子組件疊加显示,而 IndexedStack 通過 index 只显示指定索引的子組件,用法如下:

    class IndexedStackDemo extends StatefulWidget {
      @override
      _IndexedStackDemoState createState() => _IndexedStackDemoState();
    }
    
    class _IndexedStackDemoState extends State<IndexedStackDemo> {
      int _index = 0;
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            SizedBox(height: 50,),
            _buildIndexedStack(),
            SizedBox(height: 30,),
            _buildRow(),
          ],
        );
      }
    
      _buildIndexedStack() {
        return IndexedStack(
          index: _index,
          children: <Widget>[
            Center(
              child: Container(
                height: 300,
                width: 300,
                color: Colors.red,
                alignment: Alignment.center,
                child: Icon(
                  Icons.fastfood,
                  size: 60,
                  color: Colors.blue,
                ),
              ),
            ),
            Center(
              child: Container(
                height: 300,
                width: 300,
                color: Colors.green,
                alignment: Alignment.center,
                child: Icon(
                  Icons.cake,
                  size: 60,
                  color: Colors.blue,
                ),
              ),
            ),
            Center(
              child: Container(
                height: 300,
                width: 300,
                color: Colors.yellow,
                alignment: Alignment.center,
                child: Icon(
                  Icons.local_cafe,
                  size: 60,
                  color: Colors.blue,
                ),
              ),
            ),
          ],
        );
      }
    
      _buildRow() {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            IconButton(
              icon: Icon(Icons.fastfood),
              onPressed: () {
                setState(() {
                  _index = 0;
                });
              },
            ),
            IconButton(
              icon: Icon(Icons.cake),
              onPressed: () {
                setState(() {
                  _index = 1;
                });
              },
            ),
            IconButton(
              icon: Icon(Icons.local_cafe),
              onPressed: () {
                setState(() {
                  _index = 2;
                });
              },
            ),
          ],
        );
      }
    }
    

    流式布局組件

    Wrap 為子組件進行水平或者垂直方向布局,且當空間用完時,Wrap 會自動換行,也就是流式布局。

    創建多個子控件做為 Wrap 的子控件,代碼如下:

    Wrap(
      children: List.generate(10, (i) {
        double w = 50.0 + 10 * i;
        return Container(
          color: Colors.primaries[i],
          height: 50,
          width: w,
          child: Text('$i'),
        );
      }),
    )
    

    direction 屬性控制布局方向,默認為水平方向,設置方向為垂直代碼如下:

    Wrap(
      direction: Axis.vertical,
      children: List.generate(4, (i) {
        double w = 50.0 + 10 * i;
        return Container(
          color: Colors.primaries[i],
          height: 50,
          width: w,
          child: Text('$i'),
        );
      }),
    )
    

    alignment 屬性控制主軸對齊方式,crossAxisAlignment 屬性控制交叉軸對齊方式,對齊方式只對有剩餘空間的行或者列起作用,例如水平方向上正好填充完整,則不管設置主軸對齊方式為什麼,看上去的效果都是鋪滿。

    說明 :主軸就是與當前組件方向一致的軸,而交叉軸就是與當前組件方向垂直的軸,如果Wrap的布局方向為水平方向 Axis.horizontal,那麼主軸就是水平方向,反之布局方向為垂直方向 Axis.vertical ,主軸就是垂直方向。

    Wrap(
    	alignment: WrapAlignment.spaceBetween,
    	...
    )
    

    主軸對齊方式有6種,效果如下圖:

    spaceAroundspaceEvenly 區別是:

    • spaceAround:第一個子控件距開始位置和最後一個子控件距結尾位置是其他子控件間距的一半。
    • spaceEvenly:所有間距一樣。

    設置交叉軸對齊代碼如下:

    Wrap(
    	crossAxisAlignment: WrapCrossAlignment.center,
    	...
    )
    

    如果 Wrap 的主軸方向為水平方向,交叉軸方向則為垂直方向,如果想要看到交叉軸對齊方式的效果需要設置子控件的高不一樣,代碼如下:

    Wrap(
      spacing: 5,
      runSpacing: 3,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: List.generate(10, (i) {
        double w = 50.0 + 10 * i;
        double h = 50.0 + 5 * i;
        return Container(
          color: Colors.primaries[i],
          height: h,
          alignment: Alignment.center,
          width: w,
          child: Text('$i'),
        );
      }),
    )
    

    runAlignment 屬性控制 Wrap 的交叉抽方向上每一行的對齊方式,下面直接看 runAlignment 6中方式對應的效果圖,

    runAlignmentalignment 的區別:

    • alignment :是主軸方向上對齊方式,作用於每一行。
    • runAlignment :是交叉軸方向上將每一行看作一個整體的對齊方式。

    spacingrunSpacing 屬性控制Wrap主軸方向和交叉軸方向子控件之間的間隙,代碼如下:

    Wrap(
    	spacing: 5,
        runSpacing: 2,
    	...
    )
    

    textDirection 屬性表示 Wrap 主軸方向上子組件的方向,取值範圍是 ltr(從左到右) 和 rtl(從右到左),下面是從右到左的代碼:

    Wrap(
    	textDirection: TextDirection.rtl,
    	...
    )
    

    verticalDirection 屬性表示 Wrap 交叉軸方向上子組件的方向,取值範圍是 up(向上) 和 down(向下),設置代碼如下:

    Wrap(
    	verticalDirection: VerticalDirection.up,
    	...
    )
    

    注意:文字為0的組件是在下面的。

    自定義布局組件

    大部分情況下,不會使用到 Flow ,但 Flow 可以調整子組件的位置和大小,結合Matrix4繪製出各種酷炫的效果。

    Flow 組件對使用轉換矩陣操作子組件經過系統優化,性能非常高效。

    基本用法如下:

    Flow(
      delegate: SimpleFlowDelegate(),
      children: List.generate(5, (index) {
        return Container(
          height: 100,
          color: Colors.primaries[index % Colors.primaries.length],
        );
      }),
    )
    

    delegate 控制子組件的位置和大小,定義如下 :

    class SimpleFlowDelegate extends FlowDelegate {
      @override
      void paintChildren(FlowPaintingContext context) {
        for (int i = 0; i < context.childCount; ++i) {
          context.paintChild(i);
        }
      }
    
      @override
      bool shouldRepaint(SimpleFlowDelegate oldDelegate) {
        return false;
      }
    }
    

    delegate 要繼承 FlowDelegate,重寫 paintChildrenshouldRepaint 函數,上面直接繪製子組件,效果如下:

    只看到一種顏色並不是只繪製了這一個,而是疊加覆蓋了,和 Stack 類似,下面讓每一個組件有一定的偏移,SimpleFlowDelegate 修改如下:

    class SimpleFlowDelegate extends FlowDelegate {
      @override
      void paintChildren(FlowPaintingContext context) {
        for (int i = 0; i < context.childCount; ++i) {
          context.paintChild(i,transform: Matrix4.translationValues(0,i*30.0,0));
        }
      }
    
      @override
      bool shouldRepaint(SimpleFlowDelegate oldDelegate) {
        return false;
      }
    }
    

    每一個子組件比上一個組件向下偏移30。

    仿 掘金-我的效果

    效果如下:

    到拿到一個頁面時,先要將其拆分,上面的效果拆分如下:

    總體分為3個部分,水平布局,紅色區域圓形頭像代碼如下:

    _buildCircleImg() {
      return Container(
        height: 60,
        width: 60,
        decoration: BoxDecoration(
            shape: BoxShape.circle,
            image: DecorationImage(image: AssetImage('assets/images/logo.png'))),
      );
    }
    

    藍色區域代碼如下:

    _buildCenter() {
      return Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text('老孟Flutter', style: TextStyle(fontSize: 20),),
          Text('Flutter、Android', style: TextStyle(color: Colors.grey),)
        ],
      );
    }
    

    綠色區域是一個圖標,代碼如下:

    Icon(Icons.arrow_forward_ios,color: Colors.grey,size: 14,),
    

    將這3部分組合在一起:

    Container(
      color: Colors.grey.withOpacity(.5),
      alignment: Alignment.center,
      child: Container(
        height: 100,
        color: Colors.white,
        child: Row(
          children: <Widget>[
            SizedBox(
              width: 15,
            ),
            _buildCircleImg(),
            SizedBox(
              width: 25,
            ),
            Expanded(
              child: _buildCenter(),
            ),
            Icon(Icons.arrow_forward_ios,color: Colors.grey,size: 14,),
            SizedBox(
              width: 15,
            ),
          ],
        ),
      ),
    )
    

    最終的效果就是開始我們看到的效果圖。

    水平展開/收起菜單

    使用Flow實現水平展開/收起菜單的功能,代碼如下:

    class DemoFlowPopMenu extends StatefulWidget {
      @override
      _DemoFlowPopMenuState createState() => _DemoFlowPopMenuState();
    }
    
    class _DemoFlowPopMenuState extends State<DemoFlowPopMenu>
        with SingleTickerProviderStateMixin {
      //動畫必須要with這個類
      AnimationController _ctrlAnimationPopMenu; //定義動畫的變量
      IconData lastTapped = Icons.notifications;
      final List<IconData> menuItems = <IconData>[
        //菜單的icon
        Icons.home,
        Icons.new_releases,
        Icons.notifications,
        Icons.settings,
        Icons.menu,
      ];
    
      void _updateMenu(IconData icon) {
        if (icon != Icons.menu) {
          setState(() => lastTapped = icon);
        } else {
          _ctrlAnimationPopMenu.status == AnimationStatus.completed
              ? _ctrlAnimationPopMenu.reverse() //展開和收攏的效果
              : _ctrlAnimationPopMenu.forward();
        }
      }
    
      @override
      void initState() {
        super.initState();
        _ctrlAnimationPopMenu = AnimationController(
          //必須初始化動畫變量
          duration: const Duration(milliseconds: 250), //動畫時長250毫秒
          vsync: this, //SingleTickerProviderStateMixin的作用
        );
      }
    
    //生成Popmenu數據
      Widget flowMenuItem(IconData icon) {
        final double buttonDiameter =
            MediaQuery.of(context).size.width * 2 / (menuItems.length * 3);
        return Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0),
          child: RawMaterialButton(
            fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,
            splashColor: Colors.amber[100],
            shape: CircleBorder(),
            constraints: BoxConstraints.tight(Size(buttonDiameter, buttonDiameter)),
            onPressed: () {
              _updateMenu(icon);
            },
            child: Icon(icon, color: Colors.white, size: 30.0),
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Flow(
            delegate: FlowMenuDelegate(animation: _ctrlAnimationPopMenu),
            children: menuItems
                .map<Widget>((IconData icon) => flowMenuItem(icon))
                .toList(),
          ),
        );
      }
    }
    

    FlowMenuDelegate 定義如下:

    class FlowMenuDelegate extends FlowDelegate {
      FlowMenuDelegate({this.animation}) : super(repaint: animation);
      final Animation<double> animation;
    
      @override
      void paintChildren(FlowPaintingContext context) {
        double x = 50.0; //起始位置
        double y = 50.0; //橫向展開,y不變
        for (int i = 0; i < context.childCount; ++i) {
          x = context.getChildSize(i).width * i * animation.value;
          context.paintChild(
            i,
            transform: Matrix4.translationValues(x, y, 0),
          );
        }
      }
    
      @override
      bool shouldRepaint(FlowMenuDelegate oldDelegate) =>
          animation != oldDelegate.animation;
    }
    

    半圓菜單展開/收起

    代碼如下:

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    
    class DemoFlowMenu extends StatefulWidget {
      @override
      _DemoFlowMenuState createState() => _DemoFlowMenuState();
    }
    
    class _DemoFlowMenuState extends State<DemoFlowMenu>
        with TickerProviderStateMixin {
      //動畫需要這個類來混合
      //動畫變量,以及初始化和銷毀
      AnimationController _ctrlAnimationCircle;
    
      @override
      void initState() {
        super.initState();
        _ctrlAnimationCircle = AnimationController(
            //初始化動畫變量
            lowerBound: 0,
            upperBound: 80,
            duration: Duration(milliseconds: 300),
            vsync: this);
        _ctrlAnimationCircle.addListener(() => setState(() {}));
      }
    
      @override
      void dispose() {
        _ctrlAnimationCircle.dispose(); //銷毀變量,釋放資源
        super.dispose();
      }
    
      //生成Flow的數據
      List<Widget> _buildFlowChildren() {
        return List.generate(
            5,
            (index) => Container(
                  child: Icon(
                    index.isEven ? Icons.timer : Icons.ac_unit,
                    color: Colors.primaries[index % Colors.primaries.length],
                  ),
                ));
      }
    
      @override
      Widget build(BuildContext context) {
        return Stack(
          children: <Widget>[
            Positioned.fill(
              child: Flow(
                delegate: FlowAnimatedCircle(_ctrlAnimationCircle.value),
                children: _buildFlowChildren(),
              ),
            ),
            Positioned.fill(
              child: IconButton(
                icon: Icon(Icons.menu),
                onPressed: () {
                  setState(() {
                    //點擊后讓動畫可前行或回退
                    _ctrlAnimationCircle.status == AnimationStatus.completed
                        ? _ctrlAnimationCircle.reverse()
                        : _ctrlAnimationCircle.forward();
                  });
                },
              ),
            ),
          ],
        );
      }
    }
    

    FlowAnimatedCircle 代碼如下:

    class FlowAnimatedCircle extends FlowDelegate {
      final double radius; //綁定半徑,讓圓動起來
      FlowAnimatedCircle(this.radius);
    
      @override
      void paintChildren(FlowPaintingContext context) {
        if (radius == 0) {
          return;
        }
        double x = 0; //開始(0,0)在父組件的中心
        double y = 0;
        for (int i = 0; i < context.childCount; i++) {
          x = radius * cos(i * pi / (context.childCount - 1)); //根據數學得出坐標
          y = radius * sin(i * pi / (context.childCount - 1)); //根據數學得出坐標
          context.paintChild(i, transform: Matrix4.translationValues(x, -y, 0));
        } //使用Matrix定位每個子組件
      }
    
      @override
      bool shouldRepaint(FlowDelegate oldDelegate) => true;
    }
    

    交流

    老孟Flutter博客地址(330個控件用法):http://laomengit.com

    歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司“嚨底家”!

    ※推薦評價好的iphone維修中心

    聚甘新

  • 德國小城弗萊堡 交通轉型有成 35年私人汽車減半

    環境資訊中心特約記者 陳文姿報導

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司“嚨底家”!

    ※推薦評價好的iphone維修中心

    聚甘新

  • 第七屆中國國際新能源汽車論壇2017打造全球規模最大,最國際化的新能源汽車論壇

    第七屆中國國際新能源汽車論壇2017打造全球規模最大,最國際化的新能源汽車論壇

    隨著全球能源危機的加重及汽車排放引起的環境問題日益得到世界各國的重視,發展環保節能的汽車已經成為全球汽車行業的必然趨勢。

    在過去六屆新能源汽車論壇成功舉辦的基礎上,由希邁商務諮詢(上海)有限公司主辦的2017年第七屆中國國際新能源汽車論壇即將於5月17日-5月19日在上海隆重舉行。新能源汽車系列論壇成功邀請了包括國家發改委能源研究所、世界電動車協會、亞太電動車協會、世界氫能協會、世界分散式能源聯盟、中國工程院等在內的政府單位與研究機構,以及包括寶馬、賓士、奇瑞捷豹路虎、大眾、奧迪、比亞迪、上汽、北汽等在內的知名整車商,共同研討新能源汽車行業政策趨勢、技術路線及難點、基礎設施建設、商業模式等並取得了豐碩的成果,獲得了業內外人士的一致好評。

    在即將到來的2017年,組委會為感謝業內外人士對系列論壇長期以來的支援和關注,將傾情奉上相比歷屆舉辦規模最大的第六屆新能源汽車論壇,涉及七個論壇,頒獎典禮,研討會及晚宴。屆時將誠邀全球範圍內的整車製造商、電網電力公司、電池廠商、零部件供應商、核心技術提供商和政府官員近600位行業人士一起,對新能源汽車產業面臨的挑戰,機遇與對策各方面進行為期三天更深層次並具有建設和戰略性的探討

    大會亮點

    往屆參會企業分部

    大會結構

    若您對峰會有更多要求,請撥打021-6045 1760與我們聯繫,謝謝理解和支持!
    我們期待與貴單位一起出席於5月17-19日在上海舉辦的第七屆中國國際新能源汽車論壇2017,以利決策!
    想瞭解詳細內容,請登陸官方網站:http://www.ourpolaris.com/2017/nev/indexcn.html
    連絡人:Hill Zeng(曾先生)
    電話:021-6045 1760
    傳真:021-6047 5887
    郵箱: market@ourpolaris.com

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司“嚨底家”!

    ※推薦評價好的iphone維修中心

    聚甘新

  • 2016AICVCE之全球及亞太地區智慧網聯汽車產業政策及未來發展趨勢

    2016AICVCE之全球及亞太地區智慧網聯汽車產業政策及未來發展趨勢

    2016AICVCE(亞太智能網聯汽車國際峰會)於2016年12月8-9日在上海世紀皇冠假日酒店順利召開。兩天的會議共四個主題,聚集200左右的智慧網聯汽車業內人士,其中半數來自整車廠。

    首先發表演講的是來自中國自動化學會的副理事長兼秘書長王飛躍教授,王教授的另一職位為中科院自動化所複雜系統管理與控制國家重點實驗室的主任,本屆峰會上他向大家分享了智慧網聯汽車技術新思路:平行駕駛與平行測試系統的開發與驗證。

    接下來發表演講的是來自貝恩諮詢公司的全球合夥人Stephen W. Dyer博士,W. Dyer博士能說一口流利的中文,本屆峰會上他向大家分享了全球智慧網聯汽車和交通發展趨勢,闡述了消費者的消費特徵;高級駕駛輔助系統將穿透35-60%車輛,到2025年市場規模高達〜44億美元;當然智慧汽車的發展是離不開傳統整車廠和互聯網造車企業以及政府政策的共同推動,同時他們也面對著巨大的挑戰。

    來自寶馬中國無人駕駛技術負責人Maximilian Doemling先生,Doemling先生是當天第一個到會場的嘉賓,比工作人員還要早。本屆峰會上他向大家分享了寶馬在中國的無人駕駛技術路徑,主要從四個方面闡述,分別為:寶馬自動化駕駛的願景、中國獨特特徵促使自動駕駛的必要性、寶馬中國高度自動駕駛系統尖端技術、寶馬中國所面對的挑戰和對未來的展望。

    來自蔚來資本合夥人及投委會委員張君毅先生,張先生曾擔任羅蘭貝格管理諮詢全球合夥人及汽車業務負責人。本屆峰會上他向大家分享了自動駕駛發展趨勢,主要從三方面闡述,分別為自動駕駛的設備技術、自動駕駛市場發展及自動駕駛生態系統中的企業角色分工。

    來自科爾尼諮詢公司全球合夥人許健先生,為大家分享了全球智慧汽車市場發展趨勢和挑戰。

    來自北汽集團新技術研究院楊海軍先生,本屆峰會上為大家分享了大資料助力汽車智慧化和網聯化,主要闡述了在“互聯網+”和汽車行業的智慧化、網聯化成為發展趨勢的背景下,智慧網聯汽車大資料平臺的總體架構、平臺技術方向。汽車大資料的資料內容、大資料組織結構、大資料平臺涵蓋的技術方向,如通信閘道、大資料獲取、大資料ETL處理、大資料組織管理等,以及對大資料的應用方向。

    上午的會議在濃濃的政策、技術研討中結束。我們為各位大會主席和發言嘉賓點贊,也為各位到會聆聽的嘉賓點贊。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    聚甘新

  • 武漢肺炎疫情蔓延 蝙蝠餐在印尼仍熱賣

    摘錄自2020年2月11日中央社報導

    2019新型冠狀病毒疫情蔓延,儘管研究顯示,起自中國的新型冠狀病毒源頭可能是蝙蝠,並經由其他中間宿主傳給人類。不過在印尼部分地區,蝙蝠餐仍暢銷熱賣。

    整隻蝙蝠含頭翅全下鍋,佐咖哩烹煮的「燉蝙蝠」(Paniki),是印尼北蘇拉威西省(North Sulawesi)米納哈薩(Minahasa)當地民眾趨之若鶩的傳統菜餚。

    小販鐵布倫(Stenly Timbuleng)在北蘇拉威西省首府萬鴉老(Manado)以南的托莫洪(Tomohon)擺攤賣燉蝙蝠。他說:「新型冠狀病毒的疫情不影響生意,而且……持續熱賣完售。」鐵布倫平均每天可賣50至60隻蝙蝠,逢節慶可熱賣高達600隻。

     

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • Java多線程與併發基礎面試題

    CS-LogN思維導圖:記錄專業基礎 面試題
    開源地址:https://github.com/FISHers6/CS-LogN

    多線程與併發基礎

    實現多線程

    面試題1:有幾種實現線程的方法,分別是什麼

    • 1.繼承Thread類,啟動線程的唯一方法就是通過 Thread 類的 start()實例方法,start()方法是一個 native 方法,它將啟動一個新線程去執行 run()方法

    • 2.實現 Runnable 接口,重寫run()函數,作為參數放到Thread類構造函數中作為target屬性,運行start()方法

    • 線程池創建線程、Callable本質還是使Runnable創建,Callable是父輩類繼承了Runnable,線程池需傳入參數

    面試題2:實現Runnable方法好,還是繼承Thread類好

    • 實現Runnable接口更好

      • 1.單一繼承原則,如果繼承了Thread,就不能繼承其它類了,限制了可擴展性
      • 2.Thread類每次只能創建一個獨立的線程,損耗大,而Runnable能利用線程池工具來創建線程
      • 3.從代碼架構上看,run內容應該與Trhead代碼解耦

    面試題3:一個線程兩次調用start方法會出現什麼情況(考察源碼)

    • 第二次會出現異常,從start源碼上和線程生命周期上分析,一個線程start后,
      改變了threadState狀態字;而第二次再start每次會先檢查這個狀態不是0就報異常

    面試題4:既然start方法會調用run方法,為什麼我們還是要用start方法,而不是直接調用run方法呢(考察源碼)

    • 因為start后線程才會經過完整的線程生命周期,start調用native start0,虛擬機執startThread,thread_entry入口中調用Thread的run,

    面試題5:start和run有什麼區別

    • run()方法:只是普通的方法,調用run普通方法,可以重複多次調用
    • start()方法,會啟動一個線程,使得虛擬機去調用Runnable對象的run()方法,不能多次啟動同一個線程

    面試題6:start方法如何調用run方法的(考察源碼和JVM)

    • start方法調用native start0,JVM虛擬機執行startThread,在thread_entry中調用Thread的run方法

    面試題7:如何正確停止線程

    • 使用interrupt中斷通知,而不是強制,中斷通知後會讓被停止線程去決定何時停止,即把主動權交給需要被中斷的線程

    線程的生命周期

    面試題1:Java線程有哪幾種狀態 說說生命周期

    • 六種生命狀態(若time_waiting也算一種)

      • New,已創建但還尚未啟動的新線程
      • Runable,可運行狀態;對應操作系統的兩種狀態“就緒態” 和 “運行態”(分配到CPU)
      • Blocked,阻塞狀態;請求synchronized鎖未分配到時阻塞,直到獲取到monitor鎖再進入Runnable
      • Waiting,等待狀態
      • Timed waiting,限期等待
      • Terminated終止狀態
    • 線程的生命周期 狀態轉換圖

    Thread和Object類中

    與線程相關的重要方法

    面試題1:實現兩個線程交替打印奇數偶數

    面試題2:手寫生產者消費者設計模式,為什麼用該模式

    • 主要是為了解耦,匹配不同的能力

    面試題3:wait后發生了什麼,為什麼需要在同步代碼內才能使用

    • 從jvm的源碼實現上看,wait后,線程讓出佔有的cpu並釋放同步資源鎖;把自己加入到等待池,以後不會再主動參与cpu的競爭,除非被其它notify命中
    • 為了確保線程安全;另外wait會釋放資源,所以肯定要先拿到這個鎖,能進入同步代碼塊已經拿到了鎖

    面試題4:為什麼線程通信的方法wait,notify和notifyAll放在Object類,而sleep定義在Thread類里 (考察對象鎖)

    • 與對象的鎖有關,對象鎖綁定在對象的對象頭中,且放在Object里,使每個線程都可以持有多個對象的鎖

    面試題5:wait方法是屬於Object對象的,那調用Thread.wait會怎麼樣

    • 線程死的時候會自己notifyAll,釋放掉所有的持有自己對象的鎖。這個機制是實現很多同步方法的基礎。如果調用Thrad.wait,干擾了我們設計的同步業務流程

    面試題6:如何選擇notify還是notifyAll

    • 優先選用notifyAll,喚醒所有線程;除非業務需要每次只喚醒一個線程的

    面試題7:notfiy后發生的操作,notifyAll之後所有的線程都會再次搶奪鎖,如果某線程搶奪失敗怎麼辦?

    • notify后,讓waiterSet等待池中的一個線程與entry_List鎖池一級活躍線程一起競爭CPU
    • 搶奪鎖失敗後會繼續待在原鎖池或原等待池,等待競爭CPU的調度

    面試題8:sleep方法與notify/wait方法的異同點

    • 相同點:線程都會進入waiting狀態,都可以響應中斷
    • 不同點:1.所屬類不同;2.wait/notify必須用在同步方法中,且會釋放鎖;3.sleep可以指定時間

    面試題9:join方法後父線程進入什麼狀態

    • waiting狀態,join內部調用wait,子線程結束后自動調用notifyAll喚醒(jvm:exit函數)

    線程安全與性能

    面試題1:守護線程和普通線程的區別

    • 守護線程是服務於普通線程的,並且不會影響到jvm的退出

    面試題2:什麼是線程安全

    • 不管業務中遇到怎樣的多個線程訪問某對象或某方法的情況,而在編程這個業務邏輯的時候,都不需要再額外做任何額外的處理(也就是可以像單線程編程一樣),程序也可以正常運行(不會因為多線程而出錯),就可以稱為線程安全

    面試題3:有哪些線程不安全的情況,什麼原因導致的

    • 1.數據爭用、同時操作,如數據讀寫由於同時寫,非原子性操作導致運行結果錯誤,a++
    • 2.存在競爭,順序不當,如死鎖、活鎖、飢餓

    面試題4:什麼是多線程的上下文切換,及導致的後果

    • 進程線程切換要保存所需要的CPU運行環境,如寄存器、棧、全局變量等資源
    • 在頻繁的io以及搶鎖的時候,會導緻密集的上下文切換,多線程切換時,由於緩存和上下文的切換會帶來性能問題

    面試題5:多線程導致的開銷有哪些

    • 1.上下文切換開銷,如保存緩存(cache、快表等)的開銷

    • 2.同步協作的開銷(java內存模型)

      • 為了數據的正確性,同步手段往往會使用禁止編譯器優化(如指令重排序優化、鎖粗化等),性能變差
      • 使CPU內的緩存失效(比如volatile可見性讓自己線程的緩存失效后,必須使用主存來查看數據)

    Java內存模型

    面試題1:Java的代碼如何一步步轉化,最終被CPU執行的

      1. 最開始,我們編寫的Java代碼,是*.java文件
    1. 在編譯(javac命令)后,從剛才的.java文件會變出一個新的Java字節碼文件.class
    2. JVM會執行剛才生成的字節碼文件(*.class),並把字節碼文件轉化為機器指令
    3. 機器指令可以直接在CPU上執運行,也就是最終的程序執行
    • JVM實現會帶來不同的“翻譯”,不同的CPU平台的機器指令又千差萬別,無法保證併發安全的效果一致

    面試題2:單例模式的作用和適用場景

    • 單例模式:只獲取一次資源,全程序通用,節省內存和計算;保證多線程計算結果正確;方便管理;
      比如日期工具類只需要一個實例就可以,無需多個示例

    面試題3:單例模式的寫法,考察(重排序、單例和高併發的關係)

    • 餓漢式(靜態常量、靜態代碼塊)

      • 原理1:static靜態常量在類加載的時候就初始化完成了,且由jvm保證線程安全,保證了變量唯一
      • 原理2:靜態代碼塊中實例化和靜態常量類似;放在靜態代碼塊里初始化,類加載時完成;
      • 特徵:簡單,但沒有懶加載(需要時再加載)
    • 懶漢式(加synchronized鎖)

      • 對初始化的方法加synchronized鎖達到線程安全的目的,但效率低,多線程下變成了同步
      • 懶漢式取名:用到的時候才去加載
    • 雙重檢查

      • 代碼實現

        • 屬性加volatile,兩次if判斷NULL值,第二次前加類鎖
      • 優點

        • 線程安全;延遲加載;效率高
      • 為什麼用雙重而不用單層

        • 從線程安全方面、效率方面講
    • 靜態內部類

      • 需要理解靜態內部類的優點,懶漢式加載,jvm加載順序
    • 枚舉

      • 代碼實現簡單

        • public enum Singleton{
          INSTANCE;
          public void method(){}
          }
      • 保證了線程安全

        • 枚舉是一個特殊的類,經過反編譯查看,枚舉最終被編譯成一個final的類,繼承了枚舉父類。各個實例通過static定義,本質就是一個靜態的對象,所有第一次使用的時候採取加載(懶加載)
      • 避免反序列化破壞單例

        • 避免了:比如用反射就繞過了構造方法,反序列化出多個實例

    面試題4:單例模式各種寫法分別適用的場合

    • 1.最好的方法是枚舉,因枚舉被編譯成final類,用static定義靜態對象,懶加載。既保證了線程安全又避免了反序列化破壞單例
    • 2.如果程序一開始要加載的資源太多,考慮到啟動速度,就應該使用懶加載
    • 3.如果是對象的創建需要配置文件(一開始要加載其它資源),就不適合用餓漢式

    面試題5:餓漢式單例的缺點

    • 沒有懶加載(初始化時全部加載出),初始化開銷大

    面試題6:懶漢式單例的缺點

    • 雖然用到的時候才去加載,但是由於加鎖,性能低

    面試題7:單例模式的雙重檢查寫法為什麼要用double-check

    • 從代碼實現出發,保證線程安全、延遲加載效率高

    面試題8:為什麼雙重檢查要用volatile

    • 1.保證instance的可見性

      • 類初始化分成3條指令,重排序帶來NPE空虛指針問題,加volatile防止重排序
    • 2.防止初始化指令重排序

    面試題9:講一講什麼是Java的內存模型

    • 1.是一組規範,需要JVM實現遵守這個規範,以便實現安全的多線程程序
      2.volatile、synchronized、Lock等同步工具和關鍵字實現原理都用到了JMM
      3.重排序、內存可見性、原子性

    面試題10:什麼是happens-before,規則有哪些

    • 解決可見性問題的:在時間上,動作A發生在動作B之前,B保證能看見A,這就是happens-before

    • 規則

      • 1 單線程按代碼順序規則;2 鎖操作(synchronized和Lock);3volatile變量;4.JUC工具類的Happens-Before原則
      • 5.線程啟動時子線程啟動前能看到主線程run的所有內容;6.線程join主線程一定要等待子線程完成后再去做後面操作
      • 7.傳遞性 8.中斷檢測 9.對象構造方法的最後一行指令 happens-before 於 finalize() 方法的第一行指令

    面試題11:講一講volatile關鍵字

    • volatile是一種同步機制,比synchronized或者Lock相關類更輕量,因為使用volatile並不會發生上下文切換等開銷很大的行為。而加鎖時對象鎖會阻塞開銷大。
    • 可見性,如果一個變量別修飾成volatile,那麼JVM就知道了這個變量可能會被併發修改;
    • 不能保證原子性

    面試題12:volatile的適用場合及作用

    • 作用

      • 1.保證可見性 2.禁止指令重排序(單例雙重鎖時)
    • 適合場景

      • 適用場合1:boolean flag,布爾具有原子性,可再由volatile保證其可見性
      • 適用場合2:作為刷新之前變量的觸發器
      • 但不適合非原子性操作如:a++等

    面試題13:volatile和synchronized的異同

    • 1 性能開銷方面: 鎖開銷更大,volatile無加鎖阻塞開銷
      2 作用方面:volatile只能保證可見性,鎖既能保證可見性,又能保證原子性

    面試題14:什麼是內存可見性問題,為什麼存在

    • 多線程下,一個線程修改共享數據后,其它線程能否感知到修改了數據的線程的變化
    • CPU有多級緩存,導致讀的數據過期,各處理機有獨自的緩存未及時更新時,與主存內容不一致

    面試題15:主內存和本地內存的關係是什麼

    • Java 作為高級語言,屏蔽了CPU cache等底層細節,用 JMM 定義了一套讀寫內存數據的規範,雖然我們不再需要關心一級緩存和二級緩存的問題,但是,JMM 抽象了主內存和本地內存的概念。
    • 線程擁有自己的本地內存,並共享主內存的數據;線程讀寫共享數據也是通過本地內存交換的,所以才導致了可見性問題。

    面試題16:什麼是原子操作,Java的原子操作有哪些

    • 原子操作

      • 一系列的操作,要麼全部執行成功,要麼全部不執行,不會出現執行一半的情況,是不可分割的。
    • 1)除long和double之外的基本類型(int, byte, boolean, short, char, float)的”賦值操作”

    • 2)所有”引用reference的賦值操作”,不管是 32 位的機器還是 64 位的機器

    • 3)java.concurrent.Atomic.* 包中所有類的原子操作

    面試題17:long 和 double 的原子性你了解嗎

    • 在32位上的JVM上,long 和 double的操作不是原子的,但是在64位的JVM上是原子的。
    • 在32位機器上一次只能讀寫32位;而浮點數、long型有8字節64位;要分高32位和低32位兩條指令分開寫入,類似彙編語言浮點數乘法分高低位寄存器;64位不用分兩次讀寫了

    面試題18:生成對象的過程是不是原子操作

    • 不是,對象生成會生成分配空間、初始化、賦值,三條指令,有可能會被重排序,導致空指針

    面試題19:區分JVM內存結構、Java內存模型 、Java對象模型

    • Java內存模型,和Java的併發編程有關

      • 詳見面試題9
    • JVM內存結構,和Java虛擬機的運行時區域(堆棧)有關

      • 堆區、方法區(存放常量池 引用 類信息)
        棧區、本地方法棧、程序計數器

    • Java對象模型,和Java對象在虛擬機中的表現形式有關

      • 是Java對象自身的存儲模型,在方法區中Kclass類信息(虛函數表),在堆中存放new實例,在線程棧中存放引用,OOP-Klass Model

    面試題20:什麼是重排序

    • 指令實際執行順序和代碼在java文件中的順序不一致
    • 重排序的好處:提高處理速度,包括編譯器優化、指令重排序(局部性原理)

    死鎖

    面試題1:寫一個必然死鎖的例子

    • synchronized嵌套,構成請求循環

    面試題2:生產中什麼場景下會發生死鎖

    • 併發中多線程互不相讓:當兩個(或更多)線程(或進程)相互持有對方所需要的資源,又不主動釋放,導致所有人都無法繼續前進,導致程序陷入無盡的阻塞,這就是死鎖。

    面試題3:發生死鎖必須滿足哪些條件

    • 1.互斥
    • 2.請求和保持
    • 3.不可剝奪
    • 4.存儲循環等待鏈

    面試題4:如何用工具定位死鎖

    • 1.jstack命令在程序發生死鎖后,進行堆棧分析出死鎖線程
    • 2.ThreadMXbean 程序運行中發現死鎖,一旦發現死鎖可以讓用戶去打日誌

    面試題5:有哪些解決死鎖問題的策略

    • 1.死鎖語法,不讓死鎖發生

      • 破壞死鎖的四個條件之一;如:哲學家換手、轉賬換序
    • 2.死鎖避免

      • 銀行家算法、系統安全序列
    • 3.死鎖檢查與恢復

      • 適用資源請求分配圖,一段時間內檢查死鎖,有死鎖就恢復策略,採用恢復策略;
      • 恢復方法:進程終止 、資源剝奪
    • 4.鴕鳥策略(忽略死鎖)

      • 先忽略,後期再讓人工恢復

    面試題6:死鎖避免策略和檢測與恢復策略的主要思路是什麼

    • 死鎖語法

      • 破壞死鎖的四大條件之一
    • 死鎖避免

      • 找到安全序列,銀行家算法
    • 死鎖檢測與恢復

      • 資源請求分配圖

    面試題7:講一講經典的哲學家就餐問題,如何解決死鎖

    • 什麼時候死鎖

      • 哲學家各拿起自己左手邊的筷子,又去請求拿右手邊筷子循環請求時而阻塞
    • 如何解決死鎖

      • 1.一次兩隻筷子,形成原子性操作
      • 2.只允許4個人拿有筷子

    面試題8:實際開發中如何避免死鎖

    • 設置超時時間
    • 多使用併發類而不是自己設計鎖
    • 盡量降低鎖的使用粒度:用不同的鎖而不是一個鎖,鎖的範圍越小越好
    • 避免鎖的嵌套:MustDeadLock類
    • 分配資源前先看能不能收回來:銀行家算法
    • 盡量不要幾個功能用同一把鎖:專鎖專用
    • 給你的線程起個有意義的名字:debug和排查時事半功倍,框架和JDK都遵守這個最佳實踐

    面試題9:什麼是活躍性問題?活鎖、飢餓和死鎖有什麼區別

    • 活鎖

      • 雖然線程並沒有阻塞,也始終在運行(所以叫做“活”鎖,線程是“活”的),但是程序卻得不到進展,因為線程始終互相謙讓,重複做同樣的事

      • 工程中的活鎖實例:消息隊列,消息如果處理失敗,就放在隊列開頭重試,沒阻塞程序無法繼續

      • 如何解決活鎖問題

        • 加入隨機因素,以太網的指數退避算法
    • 飢餓

      • 當線程需要某些資源(例如CPU),但是卻始終得不到,可能原因是飢餓線程的優先級過低

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 如何提升自己?

    如何提升自己?

    如何提升自己?

    看完後浪,感慨良多…

    在程序員圈子,聽得最多的便是”35歲中年危機“。

    危機

    其實不僅僅存在“35歲危機”,還有“畢業危機”,“被裁員危機”,不僅僅在程序員圈子,幾乎所有圈子都是這樣,就像剛畢業的大學生說的:畢業等於失業。現在的社會飛速發展,我們常常感嘆大多數父母一代的人,智能手機玩着都比較費勁,其實也算是一種危機。其實不管任何職業,任何年齡的人,都應該保持“學習”的狀態,只有自身有了底氣,才能挺直了腰板,度過一個又一個危機。恩,做的不開心,我就換個工作…厲害的人,都是別人來請他去上班的。

    作為一個Javaer,當然也需要不斷的保持學習,特別是對於剛畢業的人,可能在找第一份工作的時候,你大廠與你擦肩而過,但是只要你對未來有一個完整的規劃,3年後,你一樣能達到你的目標。

    說了這麼多,只是為了強調學習的重要性。但是如何學習?學習什麼?這才是真正的問題。

    如何學習?

    很多人喜歡看視頻學習,記得剛學Java的時候,很多同學都會去看馬士兵,傳智博客等等。。。的確,視頻適合帶你入門,但是卻不適合進階。

    如果你是一個什麼都不知道的小白, 不知道什麼是IDE,是什麼叫配置環境變量,那麼的確可以看看視頻學習,因為它能帶你很快的上手,避免走很多坑。

    但是如果你是一個有一點項目經驗的人,那麼個人是不推薦通過視頻來學習新的知識的。第一個便是因為資源太少。除了培訓機構和各種專門為了做教育行業的人,很少有其他人會專門通過視頻介紹技術,即使有,視頻質量也難以得到保障。第二個便是效率問題,看視頻你不敢跳過,害怕錯過知識點,你也更加容易走神,因為進度掌握在別人手裡。

    所以最好的學習方式便是看資料,比如看書,看官方文檔等。

    如何看書?

    書讀百遍,其義自見。能真正把一本書看很多遍的人,一定能體會到這句話的精髓所在。

    擁有不同知識的人,看一本書的收貨一定是不一樣的。這裏可以簡單舉一個例子:一個剛學完Java基礎知識的人,看《Effective Java》的時候,可能只會死記硬背。一個擁有三年開發經驗的人,看《Effectice Java》的時候,會覺得恍然大悟。而一個擁有豐富的開發經驗的人,看《Effective Java》的時候,一定會激動的拍打着桌子:“對,當時這個坑就是這樣的”。

    當你想要了解一個知識點的時候,比如JVM,你可以先去各個網站,找一找網友推薦的書,一般比較經典的技術,都會有一本比較經典的書。比如JVM中的《深入理解Java虛擬機》。記住,如果是想深入了解的話,一定要買好書,湊字數的書,只適合你看個大概。

    挑選好一本書後,首先應該查看書的前言,然後看看目錄,了解整本書得框架以及知識點的分佈。最好帶着問題去看書。比如你看JVM,可能就是想了解大家常說的GC,JVM內存分佈,JVM調優等等,明白這些問題在書的第幾節,想想作者為什麼要把這個問題安排在這個地方?想要解答這些問題,需要明白哪些前提條件?

    做完上面的步驟后,就可以開始看書了,看一個好書,我建議一遍泛讀,兩遍精讀,三遍薄讀。

    第一遍,快速閱覽這本書,但是每個地方都要留一個印象,有問題不用怕,記在心裏。明白書的大體講了什麼,側重講了什麼,哪些是重點。更加重要的是,你在快速閱覽過程中,產生了什麼問題。

    當看完第一遍后,我不太建議立即去看第二遍,看完第一遍,應該對整個技術有個大概的了解,這個時候你應該實際去上手去做,比如JVM打打日誌看看,jps.exe,jstat.exe等調試工具用一用看看,嘗試將書中的內容應用到實際中。這個時候,你會產生更多的問題。

    第二遍,當經過一定的上手后,這個時候你就可以去看第二遍了,看第二遍的時候,心裏應該明白,你想解決什麼問題,你應該重點看哪裡。看的過程中,多想一想為什麼?想不明白的,一定上網查一查,問一問。這個過程中,其實更加推薦的是寫讀書筆記或者博客。嘗試將自己學到的東西講給別人聽,你會有意想不到的收穫。

    當看完第二遍后,就可以暫時休息了,因為一本書,寫的再好,看兩遍都會有點乏味,看完這遍后,整理下知識點,簡單回顧下。

    第三遍,第三遍應該在時間過去比較久之後再看,這一邊的速度可以很快,主要目的就是檢查你對這本書的內容的記憶程度理解的再好,都有可能會忘。每看到一部分內容,就去回想一下這部分內容的重點是什麼?有什麼值得注意的?為什麼是這樣。當你發現你都能說出來時,這本就,就已經薄成一張紙了、

    看哪些書?

    明白了怎麼看書之後,最後一個問題便是看哪些書了…

    作為一個程序員,最重要的便是基礎。基礎不牢,地動山搖。技術的迭代是非常快的,前幾年大火的C#,如今在國內需求已經比較少了,再比如現在慢慢崛起的go,想要不被時代拋棄,必須學會快速掌握一個新的知識,而這些知識其實都萬變不離其中。那便是基礎。

    掌握操作系統,能讓你快速明白JVM,多線程,NIO,零拷貝原理等。

    掌握網絡基礎,能讓你快速明白HttpSocketHttps

    當然,這裏所說的基礎,也包括一本語言的基礎,比如Java開發基礎等。

    等有了這些基礎知識,再去學習整體開發的框架,會事半功倍。

    明白了基礎的重要性,剩下的就是掌握一個高級開發工程師應該掌握的技能。

    然而,什麼才是一個高級開發工程師應該掌握的技能?

    很遺憾,我不能告訴你。因為不同方向,不同企業,不同部門,不同的業務。對一個人技能的要求,是不一樣的。

    最好的方法便是定製一個目標,然後看看你離這個目標還有多遠。

    怎麼去衡量你離這個目標還有多遠呢?最好的答案便是面試。面試犹如考試,少看哪些博眼球的文章標題為面試官問我…,製造焦慮,太偏的知識點可以簡單了解,但是別太浪費時間。不管你有沒有準備好,現在開始,準備一份你的簡歷,找一些差不多的崗位,然後接受面試官的鞭撻。總結每一次面試中,你發現你有空白的知識點,然後找一本書,看它。不用害怕簡歷沒什麼寫的,沒什麼寫的簡歷,更應該開始着手準備,機會總是給有準備的人。

    堅持上面我說的,我相信,offer會比“危機”先到一步。

    有感而發,隨便寫寫。

    —— 胖毛2020/06/19

    個人公眾號,隨便寫寫

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心