这篇文章演示几种典型的编程模式

依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中。除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对象,这篇文章演示几种典型的编程模式。

[601]将配置绑定为Options对象

Options模式采用依赖注入的方式提供Options对象,但是由依赖注入容提供的是一个IOptions<TOptions>对象,后者为我们提供承载配置选项的Options对象。Options模式的核心接口和类型定义在“Microsoft.Extensions.Options”这个NuGet包。在为创建的控制台项目添加了该NuGet包的引用后,我们定义了如下这个Profile类型。

public class Profile
{
    public Gender Gender { get; set; }
    public int Age { get; set; }
    public ContactInfo? ContactInfo { get; set; }
}

public class ContactInfo
{
    public string? EmailAddress { get; set; }
    public string? PhoneNo { get; set; }
}

public enum Gender
{
    Male,
    Female
}

我们在项目根目录下创建一个名为profile.json的配置文件,并在启动定义了如下的内容。为了使该文件能够在编译后自动复制到输出目录,我们需要将“Copy to Output Directory”属性设置为“Copy Always”。

{
    "gender": "Male",
    "age": "18",
    "contactInfo": {
        "emailAddress" : "foobar@outlook.com",
        "phoneNo": "123456789"
    }
}

在如下演示的程序中。我们调用AddJsonFile扩展方法将针对JSON配置文件(profile.json)的配置源注册到创建的ConfigurationBuilder对象上,并最终将IConfiguration对象构建出来。我们接下来创建了一个ServiceCollection对象,通过调用它的AddOptions扩展方法注册Options模式的核心服务。我们然后将创建的IConfiguration对象作为参数调用了ServiceCollection对象Configure<Profile>扩展方法,其目的在于利用这个IConfiguration对象来绑定作为Options的Profile对象。扩展方法Configure<TOptions>定义在“Microsoft.Extensions.Options.ConfigurationExtensions”这个NuGet包中,所以我们还得为演示程序添加该包的引用。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

var configuration = new ConfigurationManager();
configuration.AddJsonFile("profile.json");
var profile = new ServiceCollection()
    .AddOptions()
    .Configure<Profile>(configuration)
    .BuildServiceProvider()
    .GetRequiredService<IOptions<Profile>>().Value;
Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}");

在成功构建出作为依赖注入容器的IServiceProvider对象后,我们调用其GetRequiredService<T>扩展方法得到一个IOptions<Profile>对象,后者利用其Value属性提供所需的Profile对象。我们将这个Profile承载的相关信息输出到控制台上。程序运行后将在控制台上输出如图1所示结果。

image


图1 绑定配置生成的Profile对象

[602]具名Options的注册和提取

IOptions<TOptions>对象在整个应用范围内只能提供一个单一的Options对象,但是在很多情况下我们需要利用多个同类型的Options对象来承载不同的配置。就拿演示实例中用来表示个人信息的Profile类型来说,应用程序中可能会使用它来表示不同用户的信息,如张三、李四和王五。为了解决这个问题,我们可以在调用Configure<TOptions>方法对配置选项进行设置的时候指定一个具体的名称,然后使用IOptionsSnapshot<TOptions>来代替IOptions<TOptions>以提供指定名称的Options对象。为了演示提供针对不同用户的Profile对象,我们通过修改profile.json文件使之包含两个用户(“foo”和“bar”)的信息,具体内容如下所示。

{
  "foo": {
    "gender": "Male",
    "age": "18",
    "contactInfo": {
      "emailAddress": "foo@outlook.com",
      "phoneNo": "123"
    }
  },
  "bar": {
    "gender": "Female",
    "age": "25",
    "contactInfo": {
      "emailAddress": "bar@outlook.com",
      "phoneNo": "456"
    }
  }
}

具名Options的注册和提取体现在如下的演示程序中。如代码片段所示,在调用IServiceCollection接口的Configure<TOptions>扩展方法时,我们将注册的映射关系分别命名为“foo”和“bar”,提供原始配置数据的IConfiguration对象也由原来的ConfigurationRoot对象变成它的两个子配置节。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

var configuration = new ConfigurationManager();
configuration.AddJsonFile("profile.json");
var serviceProvider = new ServiceCollection()
    .AddOptions()
    .Configure<Profile>("foo", configuration.GetSection("foo"))
    .Configure<Profile>("bar", configuration.GetSection("bar"))
    .BuildServiceProvider();

var optionsAccessor = serviceProvider.GetRequiredService<IOptionsSnapshot<Profile>>();
Print(optionsAccessor.Get("foo"));
Print(optionsAccessor.Get("bar"));

static void Print(Profile profile)
{
    Console.WriteLine($"Gender: {profile.Gender}");
    Console.WriteLine($"Age: {profile.Age}");
    Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
    Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");
}

我们调用IServiceProvider对象的GetRequiredService<TService>扩展方法得到一个IOptionsSnapshot<TOptions>服务,并将用户名作为参数调用其Get方法得到对应的Profile对象。程序运行后,针对两个用户的基本信息将以图2所示的形式输出到控制台上。

image


图2 根据用户名提取对应的Profile对象

[603]Options与配置源的实时同步(匿名Options)

前面演示的第一个实例利用JSON文件定义了一个单一Profile对象的信息,我们现在对它做相应的修改来演示如何监控这个JSON文件,并在文件更新之后加载新的内容来生成对Profile对象进行绑定的IConfiuration对象。如下面的代码片段所示,我们在调用AddJsonFile扩展方法注册对应配置源时应将该方法的参数reloadOnChange设置为True,从而开启对对应配置文件的监控功能。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

var configuration = new ConfigurationManager();
configuration.AddJsonFile(
    path		: "profile.json",
    optional		: false,
    reloadOnChange	: true);

new ServiceCollection()
    .AddOptions()
    .Configure<Profile>(configuration)
    .BuildServiceProvider()
    .GetRequiredService<IOptionsMonitor<Profile>>()
    .OnChange(profile =>
    {
        Console.WriteLine($"Gender: {profile.Gender}");
        Console.WriteLine($"Age: {profile.Age}");
        Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
        Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");
    });
Console.Read();

我们利用作为依赖注入容器得到IOptionsMonitor<TOptions>对象,并调用它的OnChange方法注册了一个类型为Action<TOptions>的委托作为回调。该回调会在Options内容发生变化时自动执行,而作为输入的正是重新生成的Options对象。程序启动后针对配置文件的任何修改都会导致新的数据被打印在控制台上。比如我们先后修改了年龄(25)和性别(Female),新的数据将按照图3所示的形式反映在控制台上。

image

图3 及时提取新的Profile对象并应用到程序中(匿名Options)

[604]Options与配置源的实时同步(具名Options)

具名Options同样可以采用类似的编程模式来。我们在前面演示程序的基础上做了如下修改。如代码片段所示,在得到IOptionsMonitor<TOptions>服务之后,我们调用另一个OnChange方法重载注册了类型为Action<TOptions, String>的委托作为回调,该委托的第二个参数表示的正是在注册Configure<TOptions>指定的Options名称。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

var configuration = new ConfigurationManager();
configuration.AddJsonFile(
    path		: "profile.json",
    optional		: false,
    reloadOnChange	: true);

new ServiceCollection()
    .AddOptions()
    .Configure<Profile>("foo", configuration.GetSection("foo"))
    .Configure<Profile>("bar", configuration.GetSection("bar"))
    .BuildServiceProvider()
    .GetRequiredService<IOptionsMonitor<Profile>>()
    .OnChange((profile, name) =>
    {
        Console.WriteLine($"Name: {name}");
        Console.WriteLine($"Gender: {profile.Gender}");
        Console.WriteLine($"Age: {profile.Age}");
        Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
        Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");
    });
Console.Read();

改动后的程序启动之后,针对配置文件所作的任何更新都会体现在控制台上。比如我们分别修改了用户foo的年龄(25)和用户bar的性别(Male),新的内容将以图4所示的形式及时呈现在控制台上。

image

图4 及时提取新的Profile对象并应用到程序中(具名Options)

[605]用代码方式初始化Options(匿名Options)

前面演示的几个实例具有一个共同的特征,那就是都采用承载配置的IConfiguration对象来绑定Options对象。实际上Options是一个完全独立于配置系统的框架,利用配置绑定的形式来对Options对象进行初始化仅仅是该框架提供的一个小小的扩展而已。我们现在摒弃配置文件,转而采用编程的方式直接对Options进行初始化。如下面的代码片段所示,在调用IServiceCollection接口的Configure<Profile>扩展方法时,我们指定一个Action<Profile>委托来对作为Options的Profile对象进行初始化。修改后的程序运行后会同样在控制台上产生图1所示的输出结果。

using App;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

var profile = new ServiceCollection()
    .AddOptions()
    .Configure<Profile>(it =>
    {
        it.Gender 		= Gender.Male;
        it.Age 		= 18;
        it.ContactInfo 		= new ContactInfo
        {
            PhoneNo 		= "123456789",
            EmailAddress 	= "foobar@outlook.com"
        };
    })
    .BuildServiceProvider()
    .GetRequiredService<IOptions<Profile>>()
    .Value;

Console.WriteLine($"Gender: {profile.Gender}");
Console.WriteLine($"Age: {profile.Age}");
Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");

[606]用代码方式初始化Options(具名Options)

具名Options同样可以采用类似的编程方式。如果需要根据指定的名称对Options进行初始化,那么调用方法时就需要指定一个Action<TOptions,String>类型的委托对象,该委托对象的第二个参数表示Options的名称。在如下所示的代码片段中,我们通过类似的方式设置了两个用户(“foo”和“bar”)的信息,然后利用IOptionsSnapshot<Profile>服务将它们分别提取出来。该程序运行后会在控制台上产生图6-2所示的输出结果。(S606)

using App;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

var optionsAccessor = new ServiceCollection()
    .AddOptions()
    .Configure<Profile>("foo", it =>
    {
        it.Gender 		= Gender.Male;
        it.Age 		        = 18;
        it.ContactInfo 		= new ContactInfo
        {
            PhoneNo 		= "123",
            EmailAddress 	= "foo@outlook.com"
        };
    })
    .Configure<Profile>("bar", it =>
    {
        it.Gender 		= Gender.Female;
        it.Age 		        = 25;
        it.ContactInfo 		= new ContactInfo
        {
            PhoneNo 		= "456",
            EmailAddress 	= "bar@outlook.com"
        };
    })
    .BuildServiceProvider()
    .GetRequiredService<IOptionsSnapshot<Profile>>();

Print(optionsAccessor.Get("foo"));
Print(optionsAccessor.Get("bar"));

static void Print(Profile profile)
{
    Console.WriteLine($"Gender: {profile.Gender}");
    Console.WriteLine($"Age: {profile.Age}");
    Console.WriteLine($"Email Address: {profile.ContactInfo?.EmailAddress}");
    Console.WriteLine($"Phone No: {profile.ContactInfo?.PhoneNo}\n");
};

[607]针对依赖服务的Options设置

在很多情况下我们需要针对某个依赖的服务动态地初始化Options的设置,比较典型的就是根据当前的承载环境(开发、预发和产品)对Options做动态设置。我们在第5章“配置选项(上)”中演示了一系列针对日期/时间输出格式的配置,下面沿用这个场景演示如何根据当前的承载环境设置对应的Options。我们将DateTimeFormatOptions的定义进行简化,只保留如下所示的表示日期和时间格式的两个属性。

public class DateTimeFormatOptions
{
    public string DatePattern { get; set; }
    public string TimePattern { get; set; }
    public override string ToString() => $"Date: {DatePattern}; Time: {TimePattern}";
}

我们利用配置来提供当前的承载环境,具体采用的是基于命令行参数的配置源。 .NET的服务承载系统通过IHostEnvironment接口表示承载环境,具体实现类型为HostingEnvironment(该类型定义在“Microsoft.Extensions.Hosting”NuGet包中,我们需要添加针对这个包的引用)。如下面的演示程序所示,我们创建了一个ServiceCollection对象,并添加了针对IHostEnvironment接口的服务注册,具体提供的是一个根据环境名称创建的HostingEnvironment对象。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Options;

var environment = new ConfigurationBuilder()
    .AddCommandLine(args)
    .Build()["env"];

var services = new ServiceCollection();
services
.AddSingleton<IHostEnvironment>(
    new HostingEnvironment { EnvironmentName = environment })
    .AddOptions<DateTimeFormatOptions>().Configure<IHostEnvironment>(
        (options, env) => {
            if (env.IsDevelopment())
            {
                options.DatePattern = "dddd, MMMM d, yyyy";
                options.TimePattern = "M/d/yyyy";
            }
            else
            {
                options.DatePattern = "M/d/yyyy";
                options.TimePattern = "h:mm tt";
            }
        });

var options = services
    .BuildServiceProvider()
    .GetRequiredService<IOptions<DateTimeFormatOptions>>()
.Value;

Console.WriteLine(options);

我们调用了ServiceCollection对象的AddOptions<DateTimeFormatOptions>扩展方法完成了针对Options框架核心服务的注册,并利用返回的OptionsBuilder<DateTimeFormatOptions>对象对作为配置选项的DateTimeFormatOptions作相应设置。具体来说,我们调用了它的Configure<IHostEnvironment>方法利用提供的Action<DateTimeFormatOptions, IHostEnvironment>委托针对开发环境和非开发环境设置了不同的日期与时间格式。我们采用命令行的方式启动这个应用程序,并利用命令行参数设置不同的环境名称,就可以在控制台上看到图5所示的针对DateTimeFormatOptions的不同设置。

image


图5 针对承载环境的Options设置

[608]验证Options的有效性

配置选项是整个应用的全局设置,如果对它进行了错误的设置可能会造成很严重的后果,所以最好能够在使用之前进行有效性验证。接下来我们将上面的程序做了如下改动,从而演示如何对设置的日期和时间格式进行验证。

using App;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Globalization;

var config = new ConfigurationBuilder()
    .AddCommandLine(args)
    .Build();
var datePattern = config["date"];
var timePattern = config["time"];

var services = new ServiceCollection();
services.AddOptions<DateTimeFormatOptions>()
    .Configure(options =>
    {
        options.DatePattern = datePattern;
        options.TimePattern = timePattern;
    })
    .Validate(options => Validate(options.DatePattern) && Validate(options.TimePattern), "Invalid Date or Time pattern.");

try
{
    var options = services
        .BuildServiceProvider()
        .GetRequiredService<IOptions<DateTimeFormatOptions>>().Value;
    Console.WriteLine(options);
}
catch (OptionsValidationException ex)
{
    Console.WriteLine(ex.Message);
}

static bool Validate(string format)
{
    var time = new DateTime(1981, 8, 24, 2, 2, 2);
    var formatted = time.ToString(format);
    return DateTimeOffset.TryParseExact(formatted, format,null, DateTimeStyles.None, out var value) && (value.Date == time.Date || value.TimeOfDay == time.TimeOfDay);
}

上述演示实例借助配置系统以命令行的形式提供了日期和时间格式化字符串。在创建了OptionsBuilder<DateTimeFormatOptions>对象并对DateTimeFormatOptions做了相应设置之后,我们调用Validate<DateTimeFormatOptions>方法利用提供的Func<DateTimeFormatOptions,bool>委托对最终的设置进行验证。运行该程序并按照图6所示的方式指定不同的格式化字符串,系统会根据我们指定的规则来验证其有效性。

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/777272.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity 简单载具路线 Waypoint 导航

前言 在游戏开发和导航系统中&#xff0c;"waypoint" 是指路径中的一个特定位置或点。它通常用于定义一个物体或角色在场景中移动的目标位置或路径的一部分。通过一系列的 waypoints&#xff0c;可以指定复杂的移动路径和行为。以下是一些 waypoint 的具体用途&…

5款文案自动生成器,快速创作高质量文案

随着科技的发展&#xff0c;市面上出现了许多文案自动生成器&#xff0c;为我们的创作过程提供了极大的便利。无论是为了社交媒体内容创作&#xff0c;还是产品的文案的宣传&#xff0c;文案自动生成器就能为我们快速且高效地生成高质量的文案。以下将为大家分享5款备受赞誉的文…

nexus未开启匿名访问Anonymous Access,访问maven元数据maven-metadata,报401未授权Unauthorized错误

一、背景 下午在调试nexus的时候&#xff0c;其他同事不小心把匿名访问停用了&#xff0c;导致客户端android打包的时候&#xff0c;报错&#xff1a; Received status code 401 from server: Unauthorized。 访问http://192.168.xx.xx:8081/repository/public/com/xxx/xxxcor…

es6新语法

es6新语法 1 什么是ES6 JS语法分三块 ECMAScript : 基础语法BOM 浏览器对象 history location windowDOM 文档对象 document 编程语言JavaScript是ECMAScript的实现和扩展 。ECMAScript是由ECMA&#xff08;一个类似W3C的标准组织&#xff09;参与进行标准化的语法规范。ECMAS…

Selenium的这些自动化测试技巧你知道几个?

Selenium自动化测试技巧 与以前瀑布式开发模式不同&#xff0c;现在软件测试人员具有使用自动化工具执行测试用例套件的优势&#xff0c;而以前&#xff0c;测试人员习惯于通过测试脚本执行来完成测试。 但自动化测试的目的不是完全摆脱手动测试&#xff0c;而是最大程度地减少…

阶段总结——基于深度学习的三叶青图像识别

阶段总结——基于深度学习的三叶青图像识别 文章目录 一、计算机视觉图像分类系统设计二、训练模型2.1. 构建数据集2.2. 网络模型选择2.3. 图像数据增强与调参2.4. 部署模型到web端2.5. 开发图像识别小程序 三、实验结果3.1. 模型训练3.2. 模型部署 四、讨论五、参考文献&#…

js函数扩展内容---多参数,函数属性,字符串生成函数

1.多参数 在js中&#xff0c;Math.max()方法可以接受任意数量的参数&#xff0c; Math.max(1,2,3,4);//4 Math.max(1,2,3,4,5,6,7,8,9,10)//10 在max方法里面有一个rest参数&#xff0c;它接受了所有参数全部合成到了一个number数组里面&#xff0c; function rest(a,b,...a…

MSPM0G3507——读取引脚的高低电平方法(数字信号循迹模块)

SYSCFG配置 代码部分 //第一个传感器if( DL_GPIO_readPins(xunji_PORT_PIN1_PORT , xunji_PORT_PIN1_PIN )xunji_PORT_PIN1_PIN) //黑&#xff0c;不亮 高{a1;}if( DL_GPIO_readPins(xunji_PORT_PIN1_PORT , xunji_PORT…

第4-5天:30余种加密编码和资产架构端口应用CDNWAF站库分离负载均衡

文章目录 前言知识点常见加密编码等算法解析 资产架构&端口&应用&CDN&WAF&站库分离&负载均衡资产架构番外安全考虑阻碍 前言 在安全测试中常见的敏感信息密码等会采用加密方式&#xff0c;因此作为一名安全人员要了解常见加密。 知识点 主要有存储加…

Linux权限概述

一、权限概述 1.权限的基本概念 2.为什么要设置权限 3.linux用户的身份类别 4.user文件的拥有者 5.group文件所属组内用户 6.other其他用户 7.特殊用户root 二、普通权限管理 1.ls -l查看文件权限 2.文件类型以及权限解析 3.文件或文件夹的权限设置 4.通过数字给文件…

2024亚太杯中文赛B题洪水灾害的数据分析与预测原创论文分享

大家好&#xff0c;从昨天肝到现在&#xff0c;终于完成了2024年第十四届 APMCM 亚太地区大学生数学建模竞赛B题洪水灾害的数据分析与预测的完整论文啦。 实在精力有限&#xff0c;具体的讲解大家可以去讲解视频&#xff1a; 2024亚太杯中文赛B题洪水灾害预测原创论文保姆级教…

(一)优化算法-遗传算法

目录 前言 一、什么是遗传算法&#xff1f; &#xff08;一&#xff09;基本结构 &#xff08;二&#xff09;遗传操作 二、仿真过程 &#xff08;一&#xff09;主程序部分 &#xff08;二&#xff09;选择函数 &#xff08;三&#xff09;交叉函数 &#xff08;四&a…

【优化论】约束优化算法

约束优化算法是一类专门处理目标函数在存在约束条件下求解最优解的方法。为了更好地理解约束优化算法&#xff0c;我们需要了解一些核心概念和基本方法。 约束优化的核心概念 可行域&#xff08;Feasible Region&#xff09;&#xff1a; 比喻&#xff1a;想象你在一个园艺场…

软连接迁移 Docker 的默认安装(存储)目录

前言 经常我们会拿到一些别人装好的服务器&#xff0c;需要在这些系统上启动我们的docker服务。 但是这些“专业人员”呢&#xff0c;有时候就会有非常不专业的操作&#xff0c;比如他把根目录/只划分50GB&#xff0c;/home却有51TB。这个时候就会导致我们的服务器还有很多空间…

万界星空科技机械加工行业MES解决方案

机械加工行业作为制造业的重要组成部分&#xff0c;面临着生产效率、成本控制和产品质量提升等多重挑战。为了应对这些挑战&#xff0c;引入并实施制造执行系统&#xff08;MES&#xff09;成为了行业的必然选择。本文将详细介绍一种针对机械加工行业的MES解决方案&#xff0c;…

STM32-HAL-FATFS(文件系统)(没做完,stm32f103zet6(有大佬的可以在评论区说一下次板子为什么挂载失败了))

1STM32Cube配置 1-1配置时钟 1-2配置调试端口 1-3配置uart 1-4配置SDIO&#xff08;注意参数&#xff09;&#xff08;其中他的初始化的异常函数给注释&#xff0c;SD卡文件写了&#xff09; 配置了还要打开中断和DMA可在我的其他文章中看一样的 1-5配置FatFs (只改了图选中…

【Kubernetes】Pod 资源调度之亲和性调度

Pod 资源调度之亲和性调度 1.Node 亲和性调度1.1 Node 硬亲和性1.2 Node 软亲和性 2.Pod 亲和性调度2.1 Pod 硬亲和2.2 Pod 软亲和2.3 Pod 反亲和 Kubernetes 的 默认调度器 以 预选、优选、选定机制 完成将每个新的 Pod 资源绑定至为其选出的目标节点上&#xff0c;不过&#…

Javase-异常

文章目录 1. 异常概述2. 异常的继承结构3. 自定义异常4. 异常的处理5. 异常的使用6. finally语句块7. 方法覆盖与异常 1. 异常概述 什么是异常 ①什么是异常?有什么用? 1.Java中的异常是指程序运行时出现了错误或异常情况&#xff0c;导致程序无法继续正常执行的现象。例如&…

【CG】计算机图形学(Computer Graphics)基础(其壹)

0 学习视频 B站GAMES101-现代计算机图形学入门-闫令琪 1 什么是计算机图形学 1.1 什么是好的画面&#xff1f; 画面足够亮。如果全局光照做的好&#xff0c;整个画面就会亮&#xff0c;看起来很舒服。 1.2 计算机图形学涉及到的领域 数学&#xff08;透视&#xff09;投影…

java基础:面向对象(一)

一、概念 物以类聚&#xff0c;分类的思维模式&#xff0c;思考问题首先会解决问题需要哪些分类&#xff0c;然后对这些分类进行单独思考。最后&#xff0c;才对某个分类下的细节进行面向过程的思索。面向对象适合处理复杂的问题&#xff0c;适合处理需要多人协作的问题!对于描…